Chromium Code Reviews| 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 ed117afc63938a632ccaf1cd4f18d1bdd1d801e2..b8cf9b10eac9f3594185e91cc0e2f63eb40c4854 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; |
| @@ -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 |
| DartType tryPromoteToType(DartType to, DartType from) { |
| // Allow promoting to a subtype, for example: |
| // |
| @@ -764,8 +657,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; |
| } |
| @@ -891,8 +783,6 @@ 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, |
|
Leaf
2017/01/24 21:56:26
Update this comment?
Jennifer Messerly
2017/01/25 00:33:35
Done.
|
| // and type parameters. We proceed by eliminating these different classes |
| @@ -907,7 +797,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 |
| @@ -920,7 +810,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; |
| @@ -928,20 +818,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. |
|
Paul Berry
2017/01/24 17:45:13
I believe the code here is correct, but the commen
Jennifer Messerly
2017/01/25 00:33:35
Done.
|
| - return guardedSubtype(t1.futureOfType, t2, visited) && |
| - guardedSubtype(t1.type, t2, visited); |
| + 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 |
| @@ -954,8 +854,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 |
| @@ -1045,6 +945,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; |
| @@ -1418,6 +1323,9 @@ class TypeSystemImpl extends TypeSystem { |
| TypeSystemImpl(this.typeProvider); |
| @override |
| + bool get isStrong => false; |
| + |
| + @override |
| DartType tryPromoteToType(DartType to, DartType from) { |
| // Declared type should not be "dynamic". |
| // Promoted type should not be "dynamic". |
| @@ -1554,7 +1462,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. |
| @@ -1573,38 +1481,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. |
| @@ -1644,8 +1520,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) { |