diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index 3ba4d98e7fe383..9b5d045c4c793e 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -420,3 +420,393 @@ The algorithm is amended as follows: **Section** "III.4.2 callvirt" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. **Section** "III.4.18 ldvirtftn" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. + +## Static Interface Methods + +Follow proposed changes to the ECMA standard pertaining to static interface methods. The quotations and page numbers refer to +version 6 from June 2012 available at: + +https://www.ecma-international.org/publications-and-standards/standards/ecma-335/ + +### I.8.4.4, Virtual Methods + +(Add second paragraph) + +Static interface methods may be marked as virtual. Valid object types implementing such interfaces shall provide implementations +for these methods by means of Method Implementations (II.15.1.4). Polymorphic behavior of calls to these methods is facilitated +by the constrained. call IL instruction where the constrained. prefix specifies the type to use for lookup of the static interface +method. + +### II.9.7, Validity of member signatures + +(Edit bulleted list under **Generic type definition** at the top of page 134) + +* Every instance method and virtual method declaration is valid with respect to S +* Every inherited interface declaration is valid with respect to S +* There are no restrictions on *non-virtual* static members, instance constructors or on the type's +own generic parameter constraints. + +### II.9.9, Inheritance and Overriding + +(Edit first paragraph by adding the word *virtual* to the parenthesized formulation *for virtual **instance** methods*) + +Member inheritance is defined in Partition I, in “Member Inheritance”. (Overriding and hiding are also +defined in that partition, in “Hiding, overriding, and layout”.) This definition is extended, in an obvious +manner, in the presence of generics. Specifically, in order to determine whether a member hides (for +static or instance members) or overrides (for virtual instance methods) a member from a base class or interface, +simply substitute each generic parameter with its generic argument, and compare the resulting member +signatures. [*Example*: The following illustrates this point: + +### II.9.11, Constrains on Generic Parameters + +(Change first paragraph) + +A generic parameter declared on a generic class or generic method can be *constrained* by one or more +types (for encoding, see *GenericParamConstraint* table in paragraph II.22.21) and by one or more special +constraints (paragraph II.10.1.7). Generic parameters can be instantiated only with generic arguments that are +*assignable-to* (paragraph I.8.7.3) (when boxed) and *implements-all-static-interface-methods-of* (**paragraph +reference needed**) each of the declared constraints and that satisfy all specified special constraints. + +(Change the last paragraph on page 137) + +[*Note*: Constraints on a generic parameter only restrict the types that the generic parameter may +be instantiated with. Verification (see Partition III) requires that a field, property or method that a +generic parameter is known to provide through meeting a constraint, cannot be directly +accessed/called via the generic parameter unless it is first boxed (see Partition III) or the **callvirt**, +**call** or **ldftn** instruction is prefixed with the **constrained.** prefix instruction (see Partition III). *end note*] + +### II.10.3 Introducing and overriding virtual methods + +(Change first paragraph) + +A virtual method of a base type is overridden by providing a direct implementation of the method +(using a method definition, see paragraph II.15.4) and not specifying it to be newslot (paragraph II.15.4.2.3). An existing +method body can also be used to implement a given instance or static virtual declaration using the .override directive +(paragraph II.10.3.2). + +### II.10.3.2 The .override directive + +(Change first paragraph) + +The .override directive specifies that a virtual method shall be implemented (overridden), in this type, +by a virtual instance method with a different name or a non-virtual static method, but with the same signature. +This directive can be used to provide an implementation for a virtual method inherited from a base class, or +a virtual method specified in an interface implemented by this type. The .override directive specifies a Method +Implementation (MethodImpl) in the metadata (§II.15.1.4). + +(Change the third and fourth paragraph on page 148, the second and third one below the table) + +The first *TypeSpec::MethodName* pair specifies the virtual method that is being overridden, and shall +be either an inherited virtual method or a virtual method on an interface that the current type +implements. The remaining information specifies the virtual instance or non-virtual static method that +provides the implementation. + +While the syntax specified here (as well as the actual metadata format (paragraph II.22.27) allows any virtual +method to be used to provide an implementation, a conforming program shall provide a virtual instance +or static method actually implemented directly on the type. + +### II.12 Semantics of Interfaces + +(Add to the end of the 1st paragraph) + +Interfaces may define static virtual methods that get resolved at runtime based on actual types involved. +These static virtual methods must be marked as abstract in the defining interfaces. + +### II.12.2 Implementing virtual methods on interfaces + +(Edit 8th paragraph at page 158, the first unindented one below the bullet list, by +basically clarifying that "public virtual methods" only refer to "public virtual instance methods"): + +The VES shall use the following algorithm to determine the appropriate implementation of an +interface's virtual abstract methods on the open form of the class: + +* Create an interface table that has an empty list for each virtual method defined by +the interface. + +* If the interface is an explicit interface of this class: + + * If the class defines any public virtual instance methods whose name and signature + match a virtual method on the interface, then add these to the list for that + method, in type declaration order (see above). [*Note*: For an example where + the order is relevant, see Case 6 in paragraph 12.2.1. *end Note*] + + * If there are any public virtual instance methods available on this class (directly or inherited) + having the same name and signature as the interface method, and whose generic type + parameters do not exactly match any methods in the existing list for that interface + method for this class or any class in its inheritance chain, then add them (in **type + declaration order**) to the list for the corresponding methods on the interface. + + * If there are multiple methods with the same name, signature and generic type + parameters, only the last such method in **method declaration order** is added to the + list. [Note: For an example of duplicate methods, see Case 4 in paragraph 12.2.1. *end Note*] + + * Apply all MethodImpls that are specified for this class, placing explicitly specified + virtual methods into the interface list for this method, in place of those inherited or + chosen by name matching that have identical generic type parameters. If there are + multiple methods for the same interface method (i.e. with different generic type + parameters), place them in the list in **type declaration order** of the associated + interfaces. + + * If the current class is not abstract and there are any interface methods that still have + empty slots (i.e. slots with empty lists) for this class and all classes in its inheritance + chain, then the program is invalid. + +### II.12.2.1, Interface implementation examples (page 159) + +For now I'm inclined to add a completely separate section describing static interface +methods to this paragraph. The existing example is already quite sophisticated and +I think that expanding it even further with static interface methods would make it really +confusing. + +(Add at the end of the section before the closing title "End informative text" at the bottom of page 161) + +**Static interface method examples** + +We use the following interfaces to demonstrate static interface method resolution: + +``` +interface IFancyTypeName +{ + static string GetFancyTypeName(); +} + +interface IAddition +{ + static T Zero(); // Neutral element + static T Add(T a, T b); +} + +interface IMultiplicationBy +{ + static T One(); // Neutral element + static T Multiply(T a, TMultiplier b); +} + +interface IMultiplication : IMultiplicationBy +{ +} + +interface IArithmetic : IAddition, IMultiplication +{ +} +``` + +We demonstrate the basic rules of static interface method resolution on several simple classes +implementing these interfaces: + +``` +class FancyClass : IFancyTypeName +{ + public static string IFancyTypeName.GetFancyTypeName() { return "I am the fancy class"; } +} + +class DerivedFancyClass : FancyClass +{ +} + +class GenericPair : IArithmetic>, IMultiplicationBy, T> +{ + public T Component1; + public T Component2; + + public GenericPair(T component1, T component2) + { + Component1 = component1; + Component2 = component2; + } + + static GenericPair IAddition>.Zero() + { + return new GenericPair(0, 0); + } + + static GenericPair IAddition>.Add(GenericPair a, GenericPair b) + { + return new GenericPair(a.Component1 + b.Component1, a.Component2 + b.Component2); + } + + static GenericPair IMultiplicationBy, GenericPair>.One() + { + return new GenericPair(1, 1); + } + + static GenericPair IMultiplicationBy, GenericPair>.Multiply(GenericPair a, GenericPair b) + { + return new GenericPair(a.Component1 * b.Component1, a.Component2 * b.Component2); + } + + static GenericPair IMultiplicationBy, T>.Multiply(GenericPair a, T b) + { + return new GenericPair(a.Component1 * b, a.Component2 * b); + } +} + +class FancyFloatPair : GenericPair, IFancyTypeName +{ + public static string IFancyTypeName.GetFancyTypeName() { return "I am the fancy float pair"; } +} +``` + +Given these types and their content we can now demonstrate the resolution and behavior of +static interface methods on several simple algorithms: + +``` +void PrintFancyTypeName() + where T : IFancyTypeName +{ + Console.WriteLine("My fancy name is: {0}", T.GetFancyTypeName()); +} +``` + +Calling `PrintFancyTypeName()` should then output `I am the fancy class` +to the console. Likewise, `PrintFancyTypeName()` should output `I am the fancy +float pair`. In both cases the actual type parameter of the `PrintFancyTypeName` generic method +implements the `IFancyTypeName` interface and its virtual static method `GetFancyTypeName`. + +**Note**: Please note that `DerivedFancyClass` implements the `IFancyTypeName.GetFancyTypeName` +method via its base class `FancyClass`. While implementing the static interface method in a +base class is fine, this design proposal doesn't address implementing static interface methods +in the interfaces themselves or in derived interfaces akin to default interface support. + +``` +T Power(T t, uint power) + where T : IMultiplication +{ + T result = T.One(); + T powerOfT = t; + + while (power != 0) + { + if ((power & 1) != 0) + { + result = T.Multiply(result, powerOfT); + } + powerOfT = T.Multiply(powerOfT, powerOfT); + power >>= 1; + } + + return result; +} +``` + +This is an example of polymorphic math where the underlying operators can take arbitrary +form based on the types involved - you can calculate an integral power of a byte or an int, +of a float, a double, a complex number, a quaternion or a matrix without much distinction +with regard to the underlying type, you just need to be able to carry out basic arithmetic +operations. + +``` +T Exponential(T exponent) where + T : IArithmetic, + T : IMultiplicationBy + +{ + T result = T.One(); + T powerOfValue = exponent; + TFloat inverseFactorial = (TFloat)1.0; + const int NumberOfTermsInMacLaurinSeries = 6; + for (int term = 1; term <= NumberOfTermsInMacLaurinSeries; term++) + { + result = T.Add(result, (IMultiplicationBy)T.Multiply(T.powerOfValue, inverseFactorial)); + inverseFactorial /= term; + powerOfValue = T.Multiply(powerOfValue, exponent); + } + + return result; +} +``` + +Another example of polymorphic maths calculating the exponential using the Taylor series, +usable for calculating the exponential of a matrix. + +### II.15.2 Static, Instance and Virtual Methods (page 177) + +(Clarify first paragraph) + +Static methods are methods that are associated with a type, not with its instances. For +static virtual methods, the particular method to call is determined via a type lookup +based on the `constrained.` IL instruction prefix or on generic type constraints but +the call itself doesn't involve any instance or `this` pointer. + +### II.22.26, MethodDef: 0x06 + +(Edit bulleted section "This contains informative text only" starting at the bottom of page +233): + +Edit section *7.b*: Static | Virtual | !Abstract + +(Add new section 41 after the last section 40:) + +* 41. If the owner of this method is not an interface, then if Flags.Static is 1 then Flags.Virtual must be 0. + +### II.22.27, MethodImpl: 0x19 + +(Edit bulleted section "This contains informative text only" at the top of the page 237) + +Edit section 7: The method indexed by *MethodBody* shall be non-virtual if the method indexed +by MethodDeclaration is static. Otherwise it shall be virtual. + +(Add new section 14 after section 13:) + +* 14. If the method indexed by *MethodBody* has the static flag set, the method indexed by *MethodBody* must be indexed via a MethodDef and not a MemberRef. [ERROR] + +### III.2.1, constrained. - (prefix) invoke a member on a value of a variable type (page 316) + +(Change the section title to:) + +III.2.1, constrained. - (prefix) invoke an instance or static method or load method pointer to a variable type + +(Change the "Stack transition" section below the initial assembly format table to a table as follows:) + +| Prefix and instruction pair | Stack Transition +|:----------------------------------------------|:---------------- +| constrained. *thisType* callvirt *method* | ..., ptr, arg1, ... argN -> ..., ptr, arg1, ... argN +| constrained. *implementorType* call *method* | ..., arg1, ... argN -> ..., arg1, ... argN +| constrained. *implementorType* ldftn *method* | ..., ftn -> ..., ftn + +(Replace the first "Description" paragraph below the "Stack Transition" section as follows:) + +The `constrained.` prefix is permitted only on a `callvirt`, `call` +or `ldftn` instruction. When followed by the `callvirt` instruction, +the type of *ptr* must be a managed pointer (&) to *thisType*. The constrained prefix is designed +to allow `callvirt` instructions to be made in a uniform way independent of whether +*thisType* is a value type or a reference type. + +When followed by the `call` instruction or the `ldftn` instruction, +the method must refer to a virtual static method defined on an interface. The behavior of the +`constrained.` prefix is to change the method that the `call` or `ldftn` +instruction refers to to be the method on `implementorType` which implements the +virtual static method (paragraph *II.10.3*). + +(Edit the paragraph "Correctness:" second from the bottom of page 316:) + +The `constrained.` prefix will be immediately followed by a `ldftn`, `call` or `callvirt` +instruction. *thisType* shall be a valid `typedef`, `typeref`, or `typespec` metadata token. + +(Edit the paragraph "Verifiability" at the bottom of page 316:) + +For the `callvirt` instruction, the `ptr` argument will be a managed pointer (`&`) to `thisType`. +In addition all the normal verification rules of the `callvirt` instruction apply after the `ptr` +transformation as described above. This is equivalent to requiring that a boxed `thisType` must be +a subclass of the class `method` belongs to. + +The `implementorType` must be constrained to implement the interface that is the owning type of +the method. If the `constrained.` prefix is applied to a `call` or `ldftn` instruction, +`method` must be a virtual static method. + +### III.3.19, call - call a method (page 342) + +(Edit 2nd Description paragraph:) + +The metadata token carries sufficient information to determine whether the call is to a static +(non-virtual or virtual) method, an instance method, a virtual instance method, or a global function. In all of +these cases the destination address is determined entirely from the metadata token. (Contrast this with the +`callvirt` instruction for calling virtual instance methods, where the destination address also depends upon +the exact type of the instance reference pushed before the `callvirt`; see below.) + +(Edit numbered list in the middle of the page 342): + +Bullet 2: It is valid to call a virtual method using `call` (rather than `callvirt`); this indicates that +the method is to be resolved using the class specified by method rather than as +specified dynamically from the object being invoked. This is used, for example, to +compile calls to “methods on `base`” (i.e., the statically known parent class) or to virtual static methods.