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

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

Issue 1555603002: Fix downwards inference for async and generator functions. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years 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 a52736d015cea04e0182ceda91145fac082f106a..40aa326056c381835ed655475c76565eb6597aa2 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -5415,6 +5415,7 @@ class InferenceContext {
* A stack of return types for all of the enclosing
* functions and methods.
*/
+ // TODO(leafp) Handle the implicit union type for Futures
List<DartType> _returnStack = <DartType>[];
InferenceContext._(this._errorListener, TypeProvider typeProvider,
@@ -5423,6 +5424,12 @@ class InferenceContext {
/**
* Get the return type of the current enclosing function, if any.
+ *
+ * The type returned for a function is the type that is expected
+ * to be used in a return or yield context. For ordinary functions
+ * this is the same as the return type of the function. For async
+ * functions returning Future<T> and for generator functions
+ * returning Stream<T> or Iterable<T>, this is T.
*/
DartType get returnContext =>
(_returnStack.isNotEmpty) ? _returnStack.last : null;
Bob Nystrom 2015/12/30 01:03:39 Nit: the parentheses aren't needed here.
Leaf 2015/12/30 18:28:06 Done.
@@ -8360,7 +8367,8 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitAwaitExpression(AwaitExpression node) {
//TODO(leafp): Handle the implicit union type here
- DartType contextType = InferenceContext.getType(node);
+ DartType contextType = StaticTypeAnalyzer.flattenFutures(
+ typeProvider, InferenceContext.getType(node));
if (contextType != null) {
InterfaceType futureT =
typeProvider.futureType.substitute4([contextType]);
@@ -8894,7 +8902,11 @@ class ResolverVisitor extends ScopedVisitor {
DartType functionType = InferenceContext.getType(node);
if (functionType is FunctionType) {
_inferFormalParameterList(node.parameters, functionType);
- InferenceContext.setType(node.body, functionType.returnType);
+ DartType returnType = _computeReturnOrYieldType(
+ functionType.returnType,
+ _enclosingFunction.isGenerator,
+ _enclosingFunction.isAsynchronous);
+ InferenceContext.setType(node.body, returnType);
}
super.visitFunctionExpression(node);
} finally {
@@ -9099,7 +9111,11 @@ class ResolverVisitor extends ScopedVisitor {
ExecutableElement outerFunction = _enclosingFunction;
try {
_enclosingFunction = node.element;
- InferenceContext.setType(node.body, node.element.type?.returnType);
+ DartType returnType = _computeReturnOrYieldType(
+ _enclosingFunction.type?.returnType,
+ _enclosingFunction.isGenerator,
+ _enclosingFunction.isAsynchronous);
+ InferenceContext.setType(node.body, returnType);
super.visitMethodDeclaration(node);
} finally {
_enclosingFunction = outerFunction;
@@ -9328,18 +9344,15 @@ class ResolverVisitor extends ScopedVisitor {
// 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 is a yield*, then we just propagate the return type downwards
+ // If this just a yield, then we just pass on the element type
DartType type = returnType;
- // If this just a yield, then we need to get the element type
- if (node.star == null) {
+ 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 wrapperD = _enclosingFunction.isSynchronous
- ? typeProvider.iterableDynamicType
- : typeProvider.streamDynamicType;
- // Match the types to instantiate the type arguments if possible
- List<DartType> targs =
- inferenceContext.matchTypes(wrapperD, returnType);
- type = (targs?.length == 1) ? targs[0] : null;
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ type = wrapperType.substitute4(<DartType>[type]);
}
InferenceContext.setType(node.expression, type);
}
@@ -9380,6 +9393,35 @@ class ResolverVisitor extends ScopedVisitor {
}
/**
+ * Given the declared return type of a function, compute the type of the
+ * 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, isGenerator, isAsynchronous) {
Bob Nystrom 2015/12/30 01:03:39 Type annotate isGenerator and isAsynchronous?
Leaf 2015/12/30 18:28:06 Done.
+ // Ordinary functions just return their declared types.
+ if (!isGenerator && !isAsynchronous) {
+ return declaredType;
+ }
+ if (isGenerator) {
+ if (declaredType is InterfaceType) {
Bob Nystrom 2015/12/30 01:03:39 You could avoid a little indentation by flipping t
Leaf 2015/12/30 18:28:06 Done.
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
+ InterfaceType wrapperD = isAsynchronous
Bob Nystrom 2015/12/30 01:03:39 What does "D" mean here? "dynamic"? How about "raw
Leaf 2015/12/30 18:28:06 Done.
+ ? typeProvider.streamDynamicType
+ : typeProvider.iterableDynamicType;
+ // Match the types to instantiate the type arguments if possible
+ List<DartType> targs =
Bob Nystrom 2015/12/30 01:03:39 If "targs" isn't used much elsewhere, I'd use "typ
Leaf 2015/12/30 18:28:06 Done.
+ inferenceContext.matchTypes(wrapperD, declaredType);
+ return (targs?.length == 1) ? targs[0] : null;
+ }
+ // Not declared as an interface type, so leave it alone.
+ return null;
+ }
+ // Must be asynchronous to reach here, so strip off any layers of Future
+ return StaticTypeAnalyzer.flattenFutures(typeProvider, declaredType);
+ }
+
+ /**
* 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
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/resolver_test.dart » ('j') | pkg/analyzer/test/generated/resolver_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698