Index: pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
index 3f38498cdacde08770f4b4f316159012ad132f37..af157bbbc2701b4d804c8f2b9afefb29ebe60d92 100644 |
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
@@ -172,9 +172,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
/** |
* The Dart Language Specification, 16.29 (Await Expressions): |
* |
- * Let flatten(T) = flatten(S) if T = Future<S>, and T otherwise. The |
- * static type of [the expression "await e"] is flatten(T) where T is the |
- * static type of e. |
+ * The static type of [the expression "await e"] is flatten(T) where T is |
+ * the static type of e. |
*/ |
@override |
Object visitAwaitExpression(AwaitExpression node) { |
@@ -1740,15 +1739,43 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
} |
/** |
- * Implements the function "flatten" defined in the spec: "Let flatten(T) = |
- * flatten(S) if T = Future<S>, and T otherwise." |
+ * Implements the function "flatten" defined in the spec: |
+ * |
+ * If T = Future<S> then flatten(T) = flatten(S). |
+ * |
+ * Otherwise if T <: Future then let S be a type such that T << Future<S> |
+ * and for all R, if T << Future<R> then S << R. Then flatten(T) = S. |
+ * |
+ * In any other circumstance, flatten(T) = T. |
*/ |
static DartType flattenFutures(TypeProvider typeProvider, DartType type) { |
- if (type is InterfaceType && |
- type.element == typeProvider.futureType.element && |
- type.typeArguments.length > 0) { |
- return flattenFutures(typeProvider, type.typeArguments[0]); |
+ if (type is InterfaceType) { |
+ // Implement the case: "If T = Future<S> then flatten(T) = flatten(S)." |
+ if (type.element == typeProvider.futureType.element && |
+ type.typeArguments.length > 0) { |
+ return flattenFutures(typeProvider, type.typeArguments[0]); |
+ } |
+ |
+ // Implement the case: "Otherwise if T <: Future then let S be a type |
+ // such that T << Future<S> and for all R, if T << Future<R> then S << R. |
+ // Then flatten(T) = S." |
+ // |
+ // In other words, given the set of all types R such that T << Future<R>, |
+ // let S be the most specific of those types, if any such S exists. |
+ // |
+ // Since we only care about the most specific type, it is sufficent to |
+ // look at the types appearing as a parameter to Future in the type |
+ // hierarchy of T. We don't need to consider the supertypes of those |
+ // types, since they are by definition less specific. |
+ List<DartType> candidateTypes = |
+ _searchTypeHierarchyForFutureParameters(typeProvider, type); |
+ DartType flattenResult = _findMostSpecificType(candidateTypes); |
+ if (flattenResult != null) { |
+ return flattenResult; |
+ } |
} |
+ |
+ // Implement the case: "In any other circumstance, flatten(T) = T." |
return type; |
} |
@@ -1820,6 +1847,88 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
map["video"] = "VideoElement"; |
return map; |
} |
+ |
+ /** |
+ * If there is a single type which is at least as specific as all of the |
+ * types in [types], return it. Otherwise return `null`. |
+ */ |
+ static DartType _findMostSpecificType(List<DartType> types) { |
+ // The << relation ("more specific than") is a partial ordering on types, |
+ // so to find the most specific type of a set, we keep a bucket of the most |
+ // specific types seen so far such that no type in the bucket is more |
+ // specific than any other type in the bucket. |
+ List<DartType> bucket = <DartType>[]; |
+ |
+ // Then we consider each type in turn. |
+ for (DartType type in types) { |
+ // If any existing type in the bucket is more specific than this type, |
+ // then we can ignore this type. |
+ if (bucket.any((DartType t) => t.isMoreSpecificThan(type))) { |
+ continue; |
+ } |
+ // Otherwise, we need to add this type to the bucket and remove any types |
+ // that are less specific than it. |
+ bool added = false; |
+ int i = 0; |
+ while (i < bucket.length) { |
+ if (type.isMoreSpecificThan(bucket[i])) { |
+ if (added) { |
+ if (i < bucket.length - 1) { |
+ bucket[i] = bucket.removeLast(); |
+ } else { |
+ bucket.removeLast(); |
+ } |
+ } else { |
+ bucket[i] = type; |
+ i++; |
+ added = true; |
+ } |
+ } else { |
+ i++; |
+ } |
+ } |
+ if (!added) { |
+ bucket.add(type); |
+ } |
+ } |
+ |
+ // Now that we are finished, if there is exactly one type left in the |
+ // bucket, it is the most specific type. |
+ if (bucket.length == 1) { |
+ return bucket[0]; |
+ } |
+ |
+ // Otherwise, there is no single type that is more specific than the |
+ // others. |
+ return null; |
+ } |
+ |
+ /** |
+ * Given a seed type [type], search its class hierarchy for types of the form |
+ * Future<R>, and return a list of the resulting R's. |
+ */ |
+ static List<DartType> _searchTypeHierarchyForFutureParameters( |
+ TypeProvider typeProvider, InterfaceType type) { |
+ List<DartType> result = <DartType>[]; |
+ HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
+ void recurse(InterfaceType type) { |
+ if (type.element == typeProvider.futureType.element && |
+ type.typeArguments.length > 0) { |
+ result.add(type.typeArguments[0]); |
+ } |
+ if (visitedClasses.add(type.element)) { |
+ if (type.superclass != null) { |
+ recurse(type.superclass); |
+ } |
+ for (InterfaceType interface in type.interfaces) { |
+ recurse(interface); |
+ } |
+ visitedClasses.remove(type.element); |
+ } |
+ } |
+ recurse(type); |
+ return result; |
+ } |
} |
class _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction |