Skip to content

Commit

Permalink
Covariant return updates to ecma augments (#56496)
Browse files Browse the repository at this point in the history
* Covariant return updates to ecma augments
  • Loading branch information
davidwrighton authored Jul 29, 2021
1 parent 6d35185 commit 5d4d913
Showing 1 changed file with 98 additions and 0 deletions.
98 changes: 98 additions & 0 deletions docs/design/specs/Ecma-335-Augments.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

This is a list of additions and edits to be made in ECMA-335 specifications. It includes both documentation of new runtime features and issues encountered during development. Some of the issues are definite spec errors while others could be reasoned as Microsoft implementation quirks.

## Changes by feature area

- [Signatures](#signatures)
- [Heap sizes](#heap-sizes)
- [Metadata merging](#metadata-merging)
- [Module Initializer](#module-initializer)
- [Default Interface Methods](#default-interface-methods)
- [Static Interface Methods](#static-interface-methods)
- [Covariant Return Types](#covariant-return-types)

## Signatures

There is a general philosophical issue whereby the spec defines the
Expand Down Expand Up @@ -810,3 +820,91 @@ Bullet 2: It is valid to call a virtual method using `call` (rather than `callvi
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.

## Covariant Return Types

Covariant return methods is a runtime feature designed to support the [covariant return types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/covariant-returns.md) and [records](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md) C# language features implemented in C# 9.0.

This feature allows an overriding method to have a return type that is different than the one on the method it overrides, but compatible with it. The type compability rules are defined in ECMA I.8.7.1.

Example:
```
class Base
{
public virtual object VirtualMethod() { return null; }
}
class Derived : Base
{
// Note that the VirtualMethod here overrides the VirtualMethod defined on Base even though the
// return types are not the same. However, the return types are compatible in a covariant fashion.
public override string VirtualMethod() { return null;}
}
```

Covariant return methods can only be described through MethodImpl records, and are only applicable to methods on non-interface reference types. In addition the covariant override is only applicable for overriding methods also defined on a non-interface reference type.

See [Implementation Design](../features/covariant-return-methods.md) for notes from the implementation in CoreCLR.

Changes to the spec. These changes are relative to the 6th edition (June 2012) of the ECMA-335 specification published by ECMA available at:

https://www.ecma-international.org/publications-and-standards/standards/ecma-335/

### I.8.7.1 Assignment compatibility for signature types

Add a new signature type relation.

"A method signature type T is *covariant-return-compatible-with* with a method signature type U if and only if:
1. The calling conventions of T and U shall match exactly, ignoring the distinction between static and instance methods (i.e., the this parameter, if any, is not treated specially).
2. For each parameter type of P of T, and corresponding type Q of U, P is identical Q.
3. For the return type P of T, and return type Q of U, Q is *compatible-with* P and is a *reference type* OR Q is identical to P.

### II.10.3.2 The .override directive (page 147)

Edit the first paragraph "The .override directive specifies that a virtual method shall be implemented (overridden), in this type, by a virtual method with a different name, but with a *covariant-return-compatible-with* signature (§I.8.7.1). 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 and all other. If not used to provide an implementation for a virtual method inherited from a base class the signature must be identical."

### II.10.3.4 Impact of overrides on derived classes
Add a third bullet
"- If a virtual method is overridden via an .override directive or if a derived class provides an implentation and that virtual method in parent class was overriden with an .override directive where the method body of that second .override directive is decorated with `System.Runtime.CompilerServices.PreserveBaseOverridesAttribute` then the implementation of the virtual method shall be the implementation of the method body of the second .override directive as well. If this results in the implementor of the virtual method in the parent class not having a signature which is *covariant-return-compatible-with* the virtual method in the parent class, the program is not valid.


```
.class A {
.method newslot virtual RetType VirtualFunction() { ... }
}
.class B extends A {
.method virtual DerivedRetType VirtualFunction() {
.custom instance void [System.Runtime]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = ( 01 00 00 00 )
.override A.VirtualFuncion
...
}
}
.class C extends B {
.method virtual MoreDerivedRetType VirtualFunction()
{
.override A.VirtualFunction
...
}
}
.class D extends C
{
.method virtual DerivedRetTypeNotDerivedFromMoreDerivedRetType VirtualFunction() {
.custom instance void [System.Runtime]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = ( 01 00 00 00 )
.override A.VirtualFuncion
...
}
}
```
For this example, the behavior of calls on objects of various types is presented in the following table:
| Type of object | Method Invocation | Method Called | Notes |
| --- | --- | --- | --- |
| A | A::VirtualFunction() | A::VirtualFunction | |
| B | A::VirtualFunction() | B::VirtualFunction | |
| B | B::VirtualFunction() | B::VirtualFunction | |
| C | A::VirtualFunction() | C::VirtualFunction | |
| C | B::VirtualFunction() | C::VirtualFunction | |
| C | C::VirtualFunction() | C::VirtualFunction | |
| D | B::VirtualFunction() | ERROR | A program containing type D is not valid, as B::VirtualFunction would be implemented by D::VirtualFunction which is not *covariant-return-compatible-with* (§I.8.7.1) B::VirtualFunction |
"
### II.22.27
Edit rule 12 to specify that "The method signature defined by *MethodBody* shall match those defined by *MethodDeclaration* exactly if *MethodDeclaration* defines a method on an interface or be *covariant-return-compatible-with* (§I.8.7.1) if *MethodDeclaration* represents a method on a class."

0 comments on commit 5d4d913

Please sign in to comment.