Skip to content

Specify that an implicit forwarder is generated for covariant-by-class, *and* for covariant-by-declaration #925

Closed
@eernstg

Description

@eernstg

[Edit Sep 2021: Said forwarders have actually been generated for a while by the CFE; the language team decided that it should then be allowed, so the CFE does not need to report the error. Changed the title to reflect this update. The implementation is handled in dart-lang/sdk#47072.]

Cf. this comment, the language team decided that it is a compile-time error to inherit a method implementation C.f that has a parameter p which is not covariant into a class D such that the member signature of f in the interface of D has the modifier covariant on p (which just means that p is covariant-by-declaration as seen from D).

But it is not an error when p is covariant-by-class as seen from D.

Here is an example of the first situation:

class A {}
class B extends A {}

class C {
  void f(B b) {}
}

abstract class I {
  void f(covariant A a);
}

class D extends C implements I {} // Error.

Here is an example of the second situation, involving covariance-by-class:

class A {}
class B extends A {}

class C {
  void f(B b) {}
}

abstract class I<X> {
  void f(X x);
}

class D extends C implements I<B> {}  // OK.

One way to see what is going on is to consider the implementation: In the first example, it is unsound for C.f to be used directly as the implementation of D.f. This is because D.f may be called with an actual argument whose static type is A, and it is up to the implementation of D.f to check that it is actually a B, but the declaration of C.f has no information to justify the addition of such a dynamic check to the body.

In the second example the same soundness problem arises, but because of the actual usage of code with this structure we decided that we would handle the soundness issue by generating a forwarding stub D.f which checks the dynamic type of the actual argument and then invokes C.f.

A question arises in the situation where a parameter is covariant-by-class as well as covariant-by-declaration:

class A {}
class B extends A {}

class C {
  void f(B b) {}
}

abstract class I<X> {
  void f(covariant X x);
}

class D extends C implements I<A> {}  // OK.

In this situation the fact that x is covariant-by-class in I justifies the generation of a forwarding stub, but note that the stub should check that the actual argument is B and then forward by calling super.f(x), such that the invocation of C.f is sound without further argument type checks.

This mechanism is not currently specified: The language specification tacitly implies that a forwarding stub is generated in all cases (because otherwise a soundness issue would arise, and there is no explicit language making any of the cases an error).

Additional related issues: dart-lang/sdk#31580, dart-lang/sdk#31596, dart-lang/sdk#34329, dart-lang/sdk#41371.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThere is a mistake in the language specification or in an active documentspecificationtechnical-debtDealing with a part of the language which needs clarification or adjustments

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions