-
Notifications
You must be signed in to change notification settings - Fork 227
Description
The augmentation feature specification relies on the notion of two types being the same type in a couple of locations. For example, it is an error if an introductory declaration specifies a type T1 as the return type of a method m, and an augmenting declaration of the same method has a return type T2, and T1 is not the same type as T2. A similar error occurs for corresponding parameter types that are not the same type, and similarly for any other type in the signature. For example:
void foo({required int i});
augment foo({required String i}) {} // Error.It is not a problem that the return type is omitted from the augmenting declaration (it is then "inherited" from the introductory declaration), but it does give rise to a compile-time error that the parameter type is specified as two different types.
It is not specified which notion of 'same type' we must use in order to determine whether or not there is an error. Hence this issue, which is intended to select a specific, useful notion of 'same type' for this purpose.
It would not be appropriate to use mutual subtyping. In particular, if we were to do that then we could declare the return type of a method to be dynamic and then augment it as returning Object? (because they are "the same type" with respect to this criterion). The difference between those two types is highly significant to callers of this method, so we must avoid the ambiguity that appears to exist if we allow this (it won't suffice to specify unambiguously who wins, we just don't want programs to look ambiguous in this way).
We have another notion of 'same type' in the language already: Type literals are canonicalized according to the notion of 'same type' which is specified here. I'd recommend that we keep things simple and consistent, and reuse this notion of 'same type' also for the situations where types are required to be the same in an augmentation related construct.
I should mention one property which may give rise to some debate: This notion of 'same type' applies type normalization to the operands before they are compared structurally. In particular, this means that the following would not be an error:
void foo();
augment FutureOr<void> foo() {} // OK.This is OK (according to this criterion) because normalization of FutureOr<T> where T is a top type (which includes void, but also dynamic and Object? and many others) reduces FutureOr<T> to T. So we're testing void against void, which succeeds.
Another possible choice would be to use a variant of this criterion which leaves out the normalization and otherwise works the same.
@dart-lang/language-team, WDYT?