Index: pkg/analyzer/lib/src/dart/element/type.dart |
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart |
index 1b9c290665b16c8ee63d14e80b244efee4f6cf64..51fe0d53168cadfe2bbb46d3a317ba08dcc0fb89 100644 |
--- a/pkg/analyzer/lib/src/dart/element/type.dart |
+++ b/pkg/analyzer/lib/src/dart/element/type.dart |
@@ -144,8 +144,7 @@ class DynamicTypeImpl extends TypeImpl { |
/** |
* Constructor used by [CircularTypeImpl]. |
*/ |
- DynamicTypeImpl._circular() |
- : super(instance.element, Keyword.DYNAMIC.syntax); |
+ DynamicTypeImpl._circular() : super(instance.element, Keyword.DYNAMIC.syntax); |
@override |
int get hashCode => 1; |
@@ -534,28 +533,13 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
// To test this, we instantiate both types with the same (unique) type |
// variables, and see if the result is equal. |
if (typeFormals.isNotEmpty) { |
- List<DartType> instantiateTypeArgs = new List<DartType>(); |
- List<DartType> variablesThis = new List<DartType>(); |
- List<DartType> variablesOther = new List<DartType>(); |
- for (int i = 0; i < typeFormals.length; i++) { |
- TypeParameterElement pThis = typeFormals[i]; |
- TypeParameterElement pOther = otherType.typeFormals[i]; |
- TypeParameterTypeImpl pFresh = new TypeParameterTypeImpl( |
- new TypeParameterElementImpl(pThis.name, -1)); |
- instantiateTypeArgs.add(pFresh); |
- variablesThis.add(pThis.type); |
- variablesOther.add(pOther.type); |
- // Check that the bounds are equal after equating the previous |
- // bound variables. |
- if (pThis.bound?.substitute2(instantiateTypeArgs, variablesThis) != |
- pOther.bound?.substitute2(instantiateTypeArgs, variablesOther)) { |
- return false; |
- } |
+ List<DartType> freshVariables = |
+ relateTypeFormals(this, otherType, (t, s) => t == s); |
+ if (freshVariables == null) { |
+ return false; |
} |
- // After instantiation, they will no longer have typeFormals, |
- // so we will continue below. |
- return this.instantiate(instantiateTypeArgs) == |
- otherType.instantiate(instantiateTypeArgs); |
+ return instantiate(freshVariables) == |
+ otherType.instantiate(freshVariables); |
} |
return returnType == otherType.returnType && |
@@ -581,8 +565,8 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
} |
} |
- List<DartType> instantiateTypeArgs = new List<DartType>(); |
- List<DartType> variables = new List<DartType>(); |
+ List<DartType> instantiateTypeArgs = <DartType>[]; |
+ List<DartType> variables = <DartType>[]; |
buffer.write("<"); |
for (TypeParameterElement e in typeFormals) { |
if (e != typeFormals[0]) { |
@@ -717,49 +701,71 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
[bool withDynamic = false, Set<Element> visitedElements]) { |
// Note: visitedElements is only used for breaking recursion in the type |
// hierarchy; we don't use it when recursing into the function type. |
- bool relation = _trivialFunctionRelation(type); |
- if (relation != null) { |
- return relation; |
- } |
- |
- return structuralCompare(this, type, |
- (TypeImpl t, TypeImpl s) => t.isMoreSpecificThan(s, withDynamic)); |
+ return relate( |
+ this, |
+ type, |
+ (DartType t, DartType s) => |
+ (t as TypeImpl).isMoreSpecificThan(s, withDynamic), |
+ new TypeSystemImpl().instantiateToBounds); |
} |
@override |
bool isSubtypeOf(DartType type) { |
- bool relation = _trivialFunctionRelation(type); |
- if (relation != null) { |
- return relation; |
- } |
- |
- return structuralCompare( |
- this, type, (TypeImpl t, TypeImpl s) => t.isAssignableTo(s)); |
+ return relate( |
+ this, |
+ type, |
+ (DartType t, DartType s) => t.isAssignableTo(s), |
+ new TypeSystemImpl().instantiateToBounds); |
} |
/** |
- * Tests if [other] meets any of the easy relation cases for [isSubtypeOf] |
- * and [isMoreSpecificThan]. |
+ * Given two functions [f1] and [f2] where f1 and f2 are known to be |
+ * generic function types (both have type formals), this checks that they |
+ * have the same number of formals, and that those formals have bounds |
+ * (e.g. `<T extends LowerBound>`) that satisfy [relation]. |
* |
- * Returns `true` if the relation is known to hold, `false` if it isn't, or |
- * `null` if it's unknown and a deeper structural comparison is needed. |
+ * The return value will be a new list of fresh type variables, that can be |
+ * used to instantiate both function types, allowing further comparison. |
+ * For example, given `<T>T -> T` and `<U>U -> U` we can instantiate them with |
+ * `F` to get `F -> F` and `F -> F`, which we can see are equal. |
*/ |
- bool _trivialFunctionRelation(DartType other) { |
- // Trivial base cases. |
- if (other == null) { |
- return false; |
- } else if (identical(this, other) || |
- other.isDynamic || |
- other.isDartCoreFunction || |
- other.isObject) { |
- return true; |
- } else if (other is! FunctionType) { |
- return false; |
- } else if (this == other) { |
- return true; |
+ static List<DartType> relateTypeFormals( |
+ FunctionType f1, FunctionType f2, bool relation(DartType t, DartType s)) { |
+ List<TypeParameterElement> params1 = f1.typeFormals; |
+ List<TypeParameterElement> params2 = f2.typeFormals; |
+ int count = params1.length; |
+ if (params2.length != count) { |
+ return null; |
} |
- |
- return null; |
+ // We build up a substitution matching up the type parameters |
+ // from the two types, {variablesFresh/variables1} and |
+ // {variablesFresh/variables2} |
+ List<DartType> variables1 = <DartType>[]; |
+ List<DartType> variables2 = <DartType>[]; |
+ List<DartType> variablesFresh = <DartType>[]; |
+ for (int i = 0; i < count; i++) { |
+ TypeParameterElement p1 = params1[i]; |
+ TypeParameterElement p2 = params2[i]; |
+ TypeParameterElementImpl pFresh = |
+ new TypeParameterElementImpl.synthetic(p2.name); |
+ |
+ DartType variable1 = p1.type; |
+ DartType variable2 = p2.type; |
+ DartType variableFresh = new TypeParameterTypeImpl(pFresh); |
+ |
+ variables1.add(variable1); |
+ variables2.add(variable2); |
+ variablesFresh.add(variableFresh); |
+ DartType bound1 = p1.bound ?? DynamicTypeImpl.instance; |
+ DartType bound2 = p2.bound ?? DynamicTypeImpl.instance; |
+ bound1 = bound1.substitute2(variablesFresh, variables1); |
+ bound2 = bound2.substitute2(variablesFresh, variables2); |
+ pFresh.bound = bound2; |
+ if (!relation(bound2, bound1)) { |
+ return null; |
+ } |
+ } |
+ return variablesFresh; |
} |
/** |
@@ -773,11 +779,45 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
* |
* If [returnRelation] is omitted, uses [parameterRelation] for both. |
*/ |
- static bool structuralCompare(FunctionType t, FunctionType s, |
+ static bool relate( |
+ FunctionType t, |
+ DartType other, |
bool parameterRelation(DartType t, DartType s), |
- [bool returnRelation(DartType t, DartType s)]) { |
- // Test the return types. |
+ FunctionType instantiateToBounds(FunctionType t), |
+ {bool returnRelation(DartType t, DartType s)}) { |
+ |
returnRelation ??= parameterRelation; |
+ |
+ // Trivial base cases. |
+ if (other == null) { |
+ return false; |
+ } else if (identical(t, other) || |
+ other.isDynamic || |
+ other.isDartCoreFunction || |
+ other.isObject) { |
+ return true; |
+ } else if (other is! FunctionType) { |
+ return false; |
+ } |
+ |
+ // This type cast is safe, because we checked it above. |
+ FunctionType s = other as FunctionType; |
+ if (t.typeFormals.isNotEmpty) { |
+ if (s.typeFormals.isEmpty) { |
+ t = instantiateToBounds(t); |
+ } else { |
+ List<DartType> freshVariables = relateTypeFormals(t, s, returnRelation); |
+ if (freshVariables == null) { |
+ return false; |
+ } |
+ t = t.instantiate(freshVariables); |
+ s = s.instantiate(freshVariables); |
+ } |
+ } else if (s.typeFormals.isNotEmpty) { |
+ return false; |
+ } |
+ |
+ // Test the return types. |
DartType sRetType = s.returnType; |
if (!sRetType.isVoid && !returnRelation(t.returnType, sRetType)) { |
return false; |