dart-lang / dart-lang/language
Use sound approximations for non-covariant types
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
Cf. https://github.com/dart-lang/sdk/issues/33697, https://github.com/dart-lang/site-www/issues/1017, and more than a dozen issues linked from the latter.
Consider the following example:
```dart
class C {
void Function(X) f;
C(this.f);
}
void Function(X) foo(C c) => c.f;
main() {
C c = C((int i) {});
var f = foo(c);
}
```
In this example, the class `C` has a member whose type is not covariant in the type parameters of the enclosing class: If the type of `c` is `C` or `C` then the type of `c.f` is `void Function(num)` respectively `void Function(int)`, and the latter is not a subtype of the former.
This means that it is not a sound assumption that if the static type of the receiver `c` is `C` then the dynamic type of `c.f` will be a subtype of its "statically known" type `void Function(num)`.
In order to preserve soundness (in particular, to prevent that any run-time entity—like a variable, parameter, returned value, or expression result—can ever have a type which is not a subtype of its static type), we must prevent that `foo` returns the function of type `void Function(int)` which is the value of `c.f` during the invocation of `foo` in `main`.
However, there would not be any such problem if we were to add the following to the class `C` as an instance method:
```dart
void bar() {
void Function(X) f2 = f; // Safe, no dynamic checks needed.
X x = ...;
f2(x); // Safe.
}
```
The difference is that the contravariant occurrence of `X` in the type of the field `f` is no problem when the actual type can be denoted (because `X` is in scope), but when viewing an instance of `C` from outside the class, we have a clash between the covariance of the class and the contravariance of the field type. Or, in general, a clash between the covariance of the class and any non-covariant occurrence of a type variable in the signature of a member of the enclosing class. As a convenient abbreviation, we have referred to such members as "contravariant" in many discussions about this topic.
We have one obvious remedy for this category of problems: Explicitly declared variance, cf. #214, #229, #524.
However, the use of invariance makes every usage of a class less flexible (declaration-site invariance, #214) or each declaration of a variable or similar entity of such a type (use-site invariance, #229).
This issue is a request to make the use of such contravariant members statically safe, even in the case where one or more of the relevant type parameters of the receiver type is covariant.
Contributor guide
Assessment
This issue has not been assessed yet.