dart-lang / dart-lang/language

Use sound approximations for non-covariant types

Open
#296 1 comment 0 reactions 0 assignees View on GitHub
request variance
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.