Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Unified Diff: pkg/analyzer/lib/src/generated/resolver.dart

Issue 1724543002: fix #25487, infer block lambdas from return statements (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer/lib/src/generated/resolver.dart
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 496b821de8f7b985808eea72ca1e4ce9c9071f30..9f483ce4712e5989f542ac105be986e4c26d9c2b 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -4602,12 +4602,20 @@ class InferenceContext {
final TypeSystem _typeSystem;
/**
+ * When no context type is available, this will track the least upper bound
+ * of all return statements in a lambda.
+ *
+ * This will always be kept in sync with [_returnStack].
+ */
+ final List<DartType> _inferredReturn = <DartType>[];
+
+ /**
* A stack of return types for all of the enclosing
* functions and methods.
*/
// TODO(leafp) Handle the implicit union type for Futures
// https://github.com/dart-lang/sdk/issues/25322
- List<DartType> _returnStack = <DartType>[];
+ final List<DartType> _returnStack = <DartType>[];
InferenceContext._(this._errorListener, TypeProvider typeProvider,
this._typeSystem, this._inferenceHints)
@@ -4626,6 +4634,24 @@ class InferenceContext {
_returnStack.isNotEmpty ? _returnStack.last : null;
/**
+ * Records the type of the expression of a return statement.
+ *
+ * This will be used for inferring a block bodied lambda, if no context
+ * type was available.
+ */
+ void addReturnOrYieldType(DartType type) {
+ if (_returnStack.isEmpty) {
+ return;
+ }
+ DartType context = _returnStack.last;
+ if (context == null || context.isDynamic) {
+ DartType inferred = _inferredReturn.last;
+ inferred = _typeSystem.getLeastUpperBound(_typeProvider, type, inferred);
+ _inferredReturn[_inferredReturn.length - 1] = inferred;
+ }
+ }
+
+ /**
* Match type [t1] against type [t2] as follows.
* If `t1 = I<dynamic, ..., dynamic>`, then look for a supertype
* of t1 of the form `K<S0, ..., Sm>` where `t2 = K<S0', ..., Sm'>`
@@ -4638,19 +4664,31 @@ class InferenceContext {
/**
* Pop a return type off of the return stack.
+ *
+ * Also record any inferred return type using [setType], unless this node
+ * already has a context type. This recorded type will be the least upper
+ * bound of all types added with [addReturnOrYieldType].
*/
- void popReturnContext() {
- assert(_returnStack.isNotEmpty);
+ void popReturnContext(BlockFunctionBody node) {
+ assert(_returnStack.isNotEmpty && _inferredReturn.isNotEmpty);
if (_returnStack.isNotEmpty) {
_returnStack.removeLast();
}
+ if (_inferredReturn.isNotEmpty) {
+ DartType inferred = _inferredReturn.removeLast();
+ if (!inferred.isBottom) {
+ setType(node, inferred);
+ }
+ }
}
/**
- * Push a [returnType] onto the return stack.
+ * Push a block function body's return type onto the return stack.
*/
- void pushReturnContext(DartType returnType) {
+ void pushReturnContext(BlockFunctionBody node) {
+ DartType returnType = getType(node);
_returnStack.add(returnType);
+ _inferredReturn.add(BottomTypeImpl.instance);
}
/**
@@ -7657,11 +7695,11 @@ class ResolverVisitor extends ScopedVisitor {
Object visitBlockFunctionBody(BlockFunctionBody node) {
_overrideManager.enterScope();
try {
- inferenceContext.pushReturnContext(InferenceContext.getType(node));
+ inferenceContext.pushReturnContext(node);
super.visitBlockFunctionBody(node);
} finally {
_overrideManager.exitScope();
- inferenceContext.popReturnContext();
+ inferenceContext.popReturnContext(node);
}
return null;
}
@@ -8139,10 +8177,8 @@ class ResolverVisitor extends ScopedVisitor {
matchFunctionTypeParameters(node.typeParameters, functionType);
if (functionType is FunctionType) {
_inferFormalParameterList(node.parameters, functionType);
- DartType returnType = _computeReturnOrYieldType(
- functionType.returnType,
- _enclosingFunction.isGenerator,
- _enclosingFunction.isAsynchronous);
+ DartType returnType =
+ _computeReturnOrYieldType(functionType.returnType);
InferenceContext.setType(node.body, returnType);
}
}
@@ -8352,10 +8388,8 @@ class ResolverVisitor extends ScopedVisitor {
try {
_currentFunctionBody = node.body;
_enclosingFunction = node.element;
- DartType returnType = _computeReturnOrYieldType(
- _enclosingFunction.type?.returnType,
- _enclosingFunction.isGenerator,
- _enclosingFunction.isAsynchronous);
+ DartType returnType =
+ _computeReturnOrYieldType(_enclosingFunction.type?.returnType);
InferenceContext.setType(node.body, returnType);
super.visitMethodDeclaration(node);
} finally {
@@ -8451,8 +8485,19 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitReturnStatement(ReturnStatement node) {
- InferenceContext.setType(node.expression, inferenceContext.returnContext);
- return super.visitReturnStatement(node);
+ Expression e = node.expression;
+ InferenceContext.setType(e, inferenceContext.returnContext);
+ super.visitReturnStatement(node);
+ DartType type = e?.staticType;
+ // Generators cannot return values, so don't try to do any inference if
+ // we're processing erroneous code.
+ if (type != null && _enclosingFunction?.isGenerator == false) {
+ if (_enclosingFunction.isAsynchronous) {
+ type = type.flattenFutures(typeSystem);
+ }
+ inferenceContext.addReturnOrYieldType(type);
+ }
+ return null;
}
@override
@@ -8574,25 +8619,44 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitYieldStatement(YieldStatement node) {
+ Expression e = node.expression;
DartType returnType = inferenceContext.returnContext;
- if (returnType != null && _enclosingFunction != null) {
+ bool isGenerator = _enclosingFunction?.isGenerator ?? false;
+ if (returnType != null && isGenerator) {
// If we're not in a generator ([a]sync*, then we shouldn't have a yield.
// so don't infer
- if (_enclosingFunction.isGenerator) {
- // If this just a yield, then we just pass on the element type
- DartType type = returnType;
- if (node.star != null) {
- // If this is a yield*, then we wrap the element return type
- // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
- InterfaceType wrapperType = _enclosingFunction.isSynchronous
- ? typeProvider.iterableType
- : typeProvider.streamType;
- type = wrapperType.substitute4(<DartType>[type]);
- }
- InferenceContext.setType(node.expression, type);
+
+ // If this just a yield, then we just pass on the element type
+ DartType type = returnType;
+ if (node.star != null) {
+ // If this is a yield*, then we wrap the element return type
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ type = wrapperType.substitute4(<DartType>[type]);
+ }
+ InferenceContext.setType(e, type);
+
+ }
+ super.visitYieldStatement(node);
+ DartType type = e?.staticType;
+ if (type != null && isGenerator) {
+ // If this just a yield, then we just pass on the element type
+ if (node.star != null) {
+ // If this is a yield*, then we unwrap the element return type
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ List<DartType> candidates = _findImplementedTypeArgument(type, wrapperType);
+ type = InterfaceTypeImpl.findMostSpecificType(candidates, typeSystem);
+ }
+ if (type != null) {
+ inferenceContext.addReturnOrYieldType(type);
}
}
- return super.visitYieldStatement(node);
+ return null;
}
/**
@@ -8613,6 +8677,7 @@ class ResolverVisitor extends ScopedVisitor {
}
}
+
/**
* Checks each promoted variable in the current scope for compliance with the following
* specification statement:
@@ -8632,8 +8697,10 @@ class ResolverVisitor extends ScopedVisitor {
* values which should be returned or yielded as appropriate. If a type
* cannot be computed from the declared return type, return null.
*/
- DartType _computeReturnOrYieldType(
- DartType declaredType, bool isGenerator, bool isAsynchronous) {
+ DartType _computeReturnOrYieldType(DartType declaredType) {
+ bool isGenerator = _enclosingFunction.isGenerator;
+ bool isAsynchronous = _enclosingFunction.isAsynchronous;
+
// Ordinary functions just return their declared types.
if (!isGenerator && !isAsynchronous) {
return declaredType;
@@ -8648,7 +8715,7 @@ class ResolverVisitor extends ScopedVisitor {
: typeProvider.iterableDynamicType;
// Match the types to instantiate the type arguments if possible
List<DartType> typeArgs =
- inferenceContext.matchTypes(rawType, declaredType);
+ inferenceContext.matchTypes(rawType, declaredType);
Leaf 2016/02/24 15:38:40 I think this code should be changed to use your ne
Jennifer Messerly 2016/02/24 17:43:16 Ah, thanks Leaf ... that makes so much more sense!
return (typeArgs?.length == 1) ? typeArgs[0] : null;
}
// Must be asynchronous to reach here, so strip off any layers of Future
@@ -8656,6 +8723,39 @@ class ResolverVisitor extends ScopedVisitor {
}
/**
+ * Starting from t1, search its class hierarchy for types of the form
+ * `t2<R>`, and return a list of the resulting R's
+ *
+ * For example, given t1 = `List<int>` and t2 = `Iterable<T>`, this will
+ * return [int].
+ */
+ // TODO(jmesserly): this is very similar to code used for flattening futures.
+ // The only difference is, because of a lack of TypeProvider, the other method
+ // has to match the Future type by its name and library. Here was are passed
+ // in the correct type.
+ List<DartType> _findImplementedTypeArgument(DartType t1, InterfaceType t2) {
+ List<DartType> result = <DartType>[];
+ HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+ void recurse(InterfaceTypeImpl type) {
+ if (type.element == t2.element && type.typeArguments.isNotEmpty) {
+ result.add(type.typeArguments[0]);
+ }
+ if (visitedClasses.add(type.element)) {
+ if (type.superclass != null) {
+ recurse(type.superclass);
+ }
+ type.mixins.forEach(recurse);
+ type.interfaces.forEach(recurse);
+ visitedClasses.remove(type.element);
+ }
+ }
+ if (t1 is InterfaceType) {
+ recurse(t1);
+ }
+ return result;
+ }
+
+ /**
* The given expression is the expression used to compute the iterator for a
* for-each statement. Attempt to compute the type of objects that will be
* assigned to the loop variable and return that type. Return `null` if the

Powered by Google App Engine
This is Rietveld 408576698