P is a subtype match for FutureOr<Q> with respect to L under constraints
C:
If P is a subtype match for Future<Q> with respect to L under
constraints C.
Or P is not a subtype match for Future<Q> with respect to L under
constraints C
And P is a subtype match for Q with respect to L under constraints
C
The analyzer behavior is slightly different in the case where P is a subtype match for bothFuture<Q> and Q. (This happens, for instance, if P is Null). In that case, if the attempt to match P against Future<Q> generates zero constraints, but the attempt to match P against Q generates constraints, then the constraints from matching P against Q are propagated.
This has a user visible effect in the analyzer. Consider the code:
void test(Future<int> f) async {
var t = f.then((int x) {});
(await t).foo();
}
The analyzer attempts to infer the type argument for then by matching Null <: Future<T>. This causes the type constraint Null <: T to be generated, therefore t has type Future<Null>, and (await t).foo() produces an error. If I change the analyzer to follow the informal specification, it infers a type of Future<dynamic> for t, and no error is issued. Issuing an error seems like a better behavior here.
(Note that the common front end's behavior is different still: it infers a type of dynamic for t, due to dart-lang/sdk#33044.)
The local inference informal specification (https://github.com/dart-lang/sdk/pull/29371) says:
P
is a subtype match forFutureOr<Q>
with respect toL
under constraintsC
:P
is a subtype match forFuture<Q>
with respect toL
under constraintsC
.P
is not a subtype match forFuture<Q>
with respect toL
under constraintsC
P
is a subtype match forQ
with respect toL
under constraintsC
The analyzer behavior is slightly different in the case where
P
is a subtype match for bothFuture<Q>
andQ
. (This happens, for instance, ifP
isNull
). In that case, if the attempt to matchP
againstFuture<Q>
generates zero constraints, but the attempt to matchP
againstQ
generates constraints, then the constraints from matchingP
againstQ
are propagated.This has a user visible effect in the analyzer. Consider the code:
The analyzer attempts to infer the type argument for
then
by matchingNull <: Future<T>
. This causes the type constraintNull <: T
to be generated, thereforet
has typeFuture<Null>
, and(await t).foo()
produces an error. If I change the analyzer to follow the informal specification, it infers a type ofFuture<dynamic>
fort
, and no error is issued. Issuing an error seems like a better behavior here.(Note that the common front end's behavior is different still: it infers a type of
dynamic
fort
, due to dart-lang/sdk#33044.)@leafpetersen @jmesserly what do you think?