Index: pkg/analyzer/lib/src/generated/type_system.dart |
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart |
index 6be21c33318cab94d9f74017d2c13b2e307886ca..2b6012a4eb8e61022de5e507ac72e31364719341 100644 |
--- a/pkg/analyzer/lib/src/generated/type_system.dart |
+++ b/pkg/analyzer/lib/src/generated/type_system.dart |
@@ -20,8 +20,6 @@ import 'package:analyzer/src/generated/engine.dart' |
show AnalysisContext, AnalysisOptionsImpl; |
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind; |
-import 'package:analyzer/src/generated/utilities_general.dart' |
- show JenkinsSmiHash; |
bool _isBottom(DartType t, {bool dynamicIsBottom: false}) { |
return (t.isDynamic && dynamicIsBottom) || t.isBottom || t.isDartCoreNull; |
@@ -29,123 +27,15 @@ bool _isBottom(DartType t, {bool dynamicIsBottom: false}) { |
bool _isTop(DartType t, {bool dynamicIsBottom: false}) { |
// TODO(leafp): Document the rules in play here |
+ if (t.isDartAsyncFutureOr) { |
+ return _isTop((t as InterfaceType).typeArguments[0]); |
+ } |
return (t.isDynamic && !dynamicIsBottom) || t.isObject; |
} |
typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited); |
/** |
- * A special union type of `Future<T> | T` used for Strong Mode inference. |
- */ |
-class FutureUnionType extends TypeImpl { |
- // TODO(jmesserly): a Set would be better. |
- // |
- // For now we know `Future<T> | T` is the only valid use, so we can rely on |
- // the order, which simplifies some things. |
- // |
- // This will need clean up before this can function as a real union type. |
- final List<DartType> _types; |
- |
- /** |
- * Creates a union of `Future<T> | T`. |
- */ |
- FutureUnionType._(DartType type, TypeProvider provider, TypeSystem system) |
- : _types = [ |
- provider.futureType.instantiate([type]), |
- type |
- ], |
- super(null, null); |
- |
- DartType get futureOfType => _types[0]; |
- |
- @override |
- int get hashCode { |
- int hash = 0; |
- for (var t in types) { |
- hash = JenkinsSmiHash.combine(hash, t.hashCode); |
- } |
- return JenkinsSmiHash.finish(hash); |
- } |
- |
- DartType get type => _types[1]; |
- |
- Iterable<DartType> get types => _types; |
- |
- @override |
- bool operator ==(Object obj) { |
- if (obj is FutureUnionType) { |
- if (identical(obj, this)) return true; |
- return types.length == obj.types.length && |
- types.toSet().containsAll(obj.types); |
- } |
- return false; |
- } |
- |
- @override |
- void appendTo(StringBuffer buffer) { |
- buffer.write('('); |
- for (int i = 0; i < _types.length; i++) { |
- if (i != 0) { |
- buffer.write(' | '); |
- } |
- (_types[i] as TypeImpl).appendTo(buffer); |
- } |
- buffer.write(')'); |
- } |
- |
- @override |
- bool isMoreSpecificThan(DartType type, |
- [bool withDynamic = false, Set<Element> visitedElements]) => |
- throw new UnsupportedError( |
- 'Future unions are not part of the Dart 1 type system'); |
- |
- @override |
- TypeImpl pruned(List<FunctionTypeAliasElement> prune) => |
- throw new UnsupportedError('Future unions are not substituted'); |
- |
- @override |
- DartType substitute2(List<DartType> args, List<DartType> params, |
- [List<FunctionTypeAliasElement> prune]) => |
- throw new UnsupportedError('Future unions are not used in typedefs'); |
- |
- /** |
- * Creates a union of `T | Future<T>`, unless `T` is already a future or a |
- * future-union, in which case it simply returns `T`. |
- * |
- * Conceptually this is used as the inverse of the `flatten(T)` operation, |
- * defined as: |
- * |
- * - `flatten(Future<T>) -> T` |
- * - `flatten(T) -> T` |
- * |
- * Thus the inverse will give us `T | Future<T>`. |
- * |
- * If [type] is top (dynamic or Object) then the resulting union type is |
- * equivalent to top, so we simply return it. |
- * |
- * For a similar reason `Future<T> | Future<Future<T>>` is equivalent to just |
- * `Future<T>`, so we return it. Note that it is not possible to get a |
- * `Future<T>` as a result of `flatten`, so a this case likely indicates a |
- * type error in the code, but it will be reported elsewhere. |
- */ |
- static DartType from( |
- DartType type, TypeProvider provider, TypeSystem system) { |
- if (_isTop(type)) { |
- return type; |
- } |
- if (!identical(type, type.flattenFutures(system))) { |
- // As noted above, this most likely represents erroneous input. |
- return type; |
- } |
- |
- if (type is FutureUnionType) { |
- return type; |
- } |
- return new FutureUnionType._(type, provider, system); |
- } |
-} |
- |
-/** |
* Implementation of [TypeSystem] using the strong mode rules. |
* https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md |
*/ |
@@ -173,6 +63,9 @@ class StrongTypeSystemImpl extends TypeSystem { |
} |
@override |
+ bool get isStrong => true; |
+ |
+ @override |
FunctionType functionTypeToConcreteType(FunctionType t) { |
// TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function |
// type? That would allow us to implement this in the subtype relation. |
@@ -806,8 +699,7 @@ class StrongTypeSystemImpl extends TypeSystem { |
/// If [t1] or [t2] is a type parameter we are inferring, update its bound. |
/// Returns `true` if we could possibly find a compatible type, |
/// otherwise `false`. |
- bool _inferTypeParameterSubtypeOf( |
- DartType t1, DartType t2, Set<Element> visited) { |
+ bool _inferTypeParameterSubtypeOf(DartType t1, DartType t2) { |
return false; |
} |
@@ -933,12 +825,11 @@ class StrongTypeSystemImpl extends TypeSystem { |
// Guard recursive calls |
_GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf); |
- _GuardedSubtypeChecker<DartType> guardedInferTypeParameter = |
- _guard(_inferTypeParameterSubtypeOf); |
// The types are void, dynamic, bottom, interface types, function types, |
- // and type parameters. We proceed by eliminating these different classes |
- // from consideration. |
+ // FutureOr<T> and type parameters. |
+ // |
+ // We proceed by eliminating these different classes from consideration. |
// Trivially true. |
if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) || |
@@ -949,7 +840,7 @@ class StrongTypeSystemImpl extends TypeSystem { |
// Trivially false. |
if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) || |
_isBottom(t2, dynamicIsBottom: dynamicIsBottom)) { |
- return guardedInferTypeParameter(t1, t2, visited); |
+ return _inferTypeParameterSubtypeOf(t1, t2); |
} |
// S <: T where S is a type variable |
@@ -962,7 +853,7 @@ class StrongTypeSystemImpl extends TypeSystem { |
guardedSubtype(t1.bound, t2.bound, visited)) { |
return true; |
} |
- if (guardedInferTypeParameter(t1, t2, visited)) { |
+ if (_inferTypeParameterSubtypeOf(t1, t2)) { |
return true; |
} |
DartType bound = t1.element.bound; |
@@ -970,20 +861,30 @@ class StrongTypeSystemImpl extends TypeSystem { |
} |
if (t2 is TypeParameterType) { |
- return guardedInferTypeParameter(t1, t2, visited); |
+ return _inferTypeParameterSubtypeOf(t1, t2); |
} |
- if (t1 is FutureUnionType) { |
+ // Handle FutureOr<T> union type. |
+ if (t1 is InterfaceType && t1.isDartAsyncFutureOr) { |
+ var t1TypeArg = t1.typeArguments[0]; |
+ if (t2 is InterfaceType && t2.isDartAsyncFutureOr) { |
+ var t2TypeArg = t2.typeArguments[0]; |
+ // FutureOr<A> <: FutureOr<B> iff A <: B |
+ return isSubtypeOf(t1TypeArg, t2TypeArg); |
+ } |
+ |
// given t1 is Future<A> | A, then: |
- // (Future<A> | A) <: t2 iff t2 <: Future<A> and t2 <: A. |
- return guardedSubtype(t1.futureOfType, t2, visited) && |
- guardedSubtype(t1.type, t2, visited); |
+ // (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2. |
+ var t1Future = typeProvider.futureType.instantiate([t1TypeArg]); |
+ return isSubtypeOf(t1Future, t2) && isSubtypeOf(t1TypeArg, t2); |
} |
- if (t2 is FutureUnionType) { |
+ |
+ if (t2 is InterfaceType && t2.isDartAsyncFutureOr) { |
// given t2 is Future<A> | A, then: |
// t1 <: (Future<A> | A) iff t1 <: Future<A> or t1 <: A |
- return guardedSubtype(t1, t2.futureOfType, visited) || |
- guardedSubtype(t1, t2.type, visited); |
+ var t2TypeArg = t2.typeArguments[0]; |
+ var t2Future = typeProvider.futureType.instantiate([t2TypeArg]); |
+ return isSubtypeOf(t1, t2Future) || isSubtypeOf(t1, t2TypeArg); |
} |
// Void only appears as the return type of a function, and we handle it |
@@ -996,8 +897,8 @@ class StrongTypeSystemImpl extends TypeSystem { |
return t1.isVoid && t2.isVoid; |
} |
- // We've eliminated void, dynamic, bottom, and type parameters. The only |
- // cases are the combinations of interface type and function type. |
+ // We've eliminated void, dynamic, bottom, type parameters, and FutureOr. |
+ // The only cases are the combinations of interface type and function type. |
// A function type can only subtype an interface type if |
// the interface type is Function |
@@ -1087,6 +988,11 @@ class StrongTypeSystemImpl extends TypeSystem { |
*/ |
abstract class TypeSystem { |
/** |
+ * Whether the type system is strong or not. |
+ */ |
+ bool get isStrong; |
+ |
+ /** |
* The provider of types for the system |
*/ |
TypeProvider get typeProvider; |
@@ -1460,6 +1366,8 @@ class TypeSystemImpl extends TypeSystem { |
TypeSystemImpl(this.typeProvider); |
@override |
+ bool get isStrong => false; |
+ |
FunctionType functionTypeToConcreteType(FunctionType t) => t; |
/** |
@@ -1596,7 +1504,7 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { |
if (declaredUpperBound != null) { |
// Assert that the type parameter is a subtype of its bound. |
_inferTypeParameterSubtypeOf(typeParam, |
- declaredUpperBound.substitute2(inferredTypes, fnTypeParams), null); |
+ declaredUpperBound.substitute2(inferredTypes, fnTypeParams)); |
} |
// Now we've computed lower and upper bounds for each type parameter. |
@@ -1615,38 +1523,6 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { |
DartType lowerBound = bound.lower; |
DartType upperBound = bound.upper; |
- // Collapse future unions if we inferred them somehow. |
- // |
- // TODO(jmesserly): in our initial upward phase it would be fine to |
- // keep the FutureUnionType for the argument downward context. |
- // |
- // We need to track in `inferGenericFunctionCall` whether we are going up |
- // or down. This would also allow for an improved heuristic: if we are |
- // doing our final inference, the downward context can take priority. |
- if (lowerBound is FutureUnionType) { |
- // lowerBound <: T, where lowerBound is Future<A> | A. |
- // So we choose lowerBound as LUB(A, Future<A>). |
- // |
- // This will typically lead to top with the current rules, but it will |
- // work with `bottom` or if we remove Future flattening. |
- var f = lowerBound as FutureUnionType; |
- lowerBound = _typeSystem.getLeastUpperBound(f.futureOfType, f.type); |
- } |
- if (upperBound is FutureUnionType) { |
- // T <: upperBound, where upperBound is Future<A> | A. |
- // Therefore we need T <: Future<A> or T <: A. |
- // |
- // This is just an arbitrarily heuristic. |
- var f = upperBound as FutureUnionType; |
- if (_typeSystem.isSubtypeOf(lowerBound, f.type)) { |
- upperBound = f.type; |
- } else if (_typeSystem.isSubtypeOf(lowerBound, f.futureOfType)) { |
- upperBound = f.futureOfType; |
- } else { |
- upperBound = f.type; |
- } |
- } |
- |
// See if the bounds can be satisfied. |
// TODO(jmesserly): also we should have an error for unconstrained type |
// parameters, rather than silently inferring dynamic. |
@@ -1686,8 +1562,7 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { |
} |
@override |
- bool _inferTypeParameterSubtypeOf( |
- DartType t1, DartType t2, Set<Element> visited) { |
+ bool _inferTypeParameterSubtypeOf(DartType t1, DartType t2) { |
if (t1 is TypeParameterType) { |
_TypeParameterBound bound = _bounds[t1]; |
if (bound != null) { |