Skip to content

Commit a5addef

Browse files
committed
Fix strong mode downwards inference for async and generator functions.
Downwards inference pushes the declared return type of a function down into the body in order to infer types for the returned or yielded values. The asynchronous and generator cases were not being fully handled correctly. This CL changes the return context stack kept by the inference context to contain the type expected to be returned or yielded (rather than the declared return type). At each return/yield statement, this return/yield type is used to infer types for the returned/yielded expression. At each yield* statement, the return/yield type is wrapped into the appropriate stream/iterable type. This CL also adds future flattening when doing downwards inference on awaited expressions to avoid inferring nested future types. BUG= [email protected] Review URL: https://codereview.chromium.org/1555603002 .
1 parent 6d0be9b commit a5addef

File tree

2 files changed

+359
-52
lines changed

2 files changed

+359
-52
lines changed

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5415,6 +5415,8 @@ class InferenceContext {
54155415
* A stack of return types for all of the enclosing
54165416
* functions and methods.
54175417
*/
5418+
// TODO(leafp) Handle the implicit union type for Futures
5419+
// https://github.com/dart-lang/sdk/issues/25322
54185420
List<DartType> _returnStack = <DartType>[];
54195421

54205422
InferenceContext._(this._errorListener, TypeProvider typeProvider,
@@ -5423,9 +5425,15 @@ class InferenceContext {
54235425

54245426
/**
54255427
* Get the return type of the current enclosing function, if any.
5428+
*
5429+
* The type returned for a function is the type that is expected
5430+
* to be used in a return or yield context. For ordinary functions
5431+
* this is the same as the return type of the function. For async
5432+
* functions returning Future<T> and for generator functions
5433+
* returning Stream<T> or Iterable<T>, this is T.
54265434
*/
54275435
DartType get returnContext =>
5428-
(_returnStack.isNotEmpty) ? _returnStack.last : null;
5436+
_returnStack.isNotEmpty ? _returnStack.last : null;
54295437

54305438
/**
54315439
* Match type [t1] against type [t2] as follows.
@@ -8359,8 +8367,10 @@ class ResolverVisitor extends ScopedVisitor {
83598367

83608368
@override
83618369
Object visitAwaitExpression(AwaitExpression node) {
8362-
//TODO(leafp): Handle the implicit union type here
8363-
DartType contextType = InferenceContext.getType(node);
8370+
// TODO(leafp): Handle the implicit union type here
8371+
// https://github.com/dart-lang/sdk/issues/25322
8372+
DartType contextType = StaticTypeAnalyzer.flattenFutures(
8373+
typeProvider, InferenceContext.getType(node));
83648374
if (contextType != null) {
83658375
InterfaceType futureT =
83668376
typeProvider.futureType.substitute4([contextType]);
@@ -8894,7 +8904,11 @@ class ResolverVisitor extends ScopedVisitor {
88948904
DartType functionType = InferenceContext.getType(node);
88958905
if (functionType is FunctionType) {
88968906
_inferFormalParameterList(node.parameters, functionType);
8897-
InferenceContext.setType(node.body, functionType.returnType);
8907+
DartType returnType = _computeReturnOrYieldType(
8908+
functionType.returnType,
8909+
_enclosingFunction.isGenerator,
8910+
_enclosingFunction.isAsynchronous);
8911+
InferenceContext.setType(node.body, returnType);
88988912
}
88998913
super.visitFunctionExpression(node);
89008914
} finally {
@@ -9099,7 +9113,11 @@ class ResolverVisitor extends ScopedVisitor {
90999113
ExecutableElement outerFunction = _enclosingFunction;
91009114
try {
91019115
_enclosingFunction = node.element;
9102-
InferenceContext.setType(node.body, node.element.type?.returnType);
9116+
DartType returnType = _computeReturnOrYieldType(
9117+
_enclosingFunction.type?.returnType,
9118+
_enclosingFunction.isGenerator,
9119+
_enclosingFunction.isAsynchronous);
9120+
InferenceContext.setType(node.body, returnType);
91039121
super.visitMethodDeclaration(node);
91049122
} finally {
91059123
_enclosingFunction = outerFunction;
@@ -9328,18 +9346,15 @@ class ResolverVisitor extends ScopedVisitor {
93289346
// If we're not in a generator ([a]sync*, then we shouldn't have a yield.
93299347
// so don't infer
93309348
if (_enclosingFunction.isGenerator) {
9331-
// If this is a yield*, then we just propagate the return type downwards
9349+
// If this just a yield, then we just pass on the element type
93329350
DartType type = returnType;
9333-
// If this just a yield, then we need to get the element type
9334-
if (node.star == null) {
9351+
if (node.star != null) {
9352+
// If this is a yield*, then we wrap the element return type
93359353
// If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
9336-
InterfaceType wrapperD = _enclosingFunction.isSynchronous
9337-
? typeProvider.iterableDynamicType
9338-
: typeProvider.streamDynamicType;
9339-
// Match the types to instantiate the type arguments if possible
9340-
List<DartType> targs =
9341-
inferenceContext.matchTypes(wrapperD, returnType);
9342-
type = (targs?.length == 1) ? targs[0] : null;
9354+
InterfaceType wrapperType = _enclosingFunction.isSynchronous
9355+
? typeProvider.iterableType
9356+
: typeProvider.streamType;
9357+
type = wrapperType.substitute4(<DartType>[type]);
93439358
}
93449359
InferenceContext.setType(node.expression, type);
93459360
}
@@ -9379,6 +9394,34 @@ class ResolverVisitor extends ScopedVisitor {
93799394
}
93809395
}
93819396

9397+
/**
9398+
* Given the declared return type of a function, compute the type of the
9399+
* values which should be returned or yielded as appropriate. If a type
9400+
* cannot be computed from the declared return type, return null.
9401+
*/
9402+
DartType _computeReturnOrYieldType(
9403+
DartType declaredType, bool isGenerator, bool isAsynchronous) {
9404+
// Ordinary functions just return their declared types.
9405+
if (!isGenerator && !isAsynchronous) {
9406+
return declaredType;
9407+
}
9408+
if (isGenerator) {
9409+
if (declaredType is! InterfaceType) {
9410+
return null;
9411+
}
9412+
// If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
9413+
InterfaceType rawType = isAsynchronous
9414+
? typeProvider.streamDynamicType
9415+
: typeProvider.iterableDynamicType;
9416+
// Match the types to instantiate the type arguments if possible
9417+
List<DartType> typeArgs =
9418+
inferenceContext.matchTypes(rawType, declaredType);
9419+
return (typeArgs?.length == 1) ? typeArgs[0] : null;
9420+
}
9421+
// Must be asynchronous to reach here, so strip off any layers of Future
9422+
return StaticTypeAnalyzer.flattenFutures(typeProvider, declaredType);
9423+
}
9424+
93829425
/**
93839426
* The given expression is the expression used to compute the iterator for a
93849427
* for-each statement. Attempt to compute the type of objects that will be

0 commit comments

Comments
 (0)