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..076d551523c04d785402ed714f1f36d32d4c88ab 100644 |
--- a/pkg/analyzer/lib/src/dart/element/type.dart |
+++ b/pkg/analyzer/lib/src/dart/element/type.dart |
@@ -534,28 +534,7 @@ 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; |
- } |
- } |
- // After instantiation, they will no longer have typeFormals, |
- // so we will continue below. |
- return this.instantiate(instantiateTypeArgs) == |
- otherType.instantiate(instantiateTypeArgs); |
+ return structuralCompareGeneric(this, otherType, (t, s) => t == s); |
} |
return returnType == otherType.returnType && |
@@ -722,8 +701,11 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
return relation; |
} |
Leaf
2016/02/09 21:29:25
Why does this ignore the generic case? If just no
Jennifer Messerly
2016/02/10 22:42:07
Good catch. Fixed!
|
- return structuralCompare(this, type, |
- (TypeImpl t, TypeImpl s) => t.isMoreSpecificThan(s, withDynamic)); |
+ return structuralCompare( |
+ this, |
+ type, |
+ (DartType t, DartType s) => |
Leaf
2016/02/09 21:29:25
Non-actionable comment, but the TypeImpl/Type dist
Jennifer Messerly
2016/02/10 22:42:07
Yeah, definitely. I'm trying to get things strong
|
+ (t as TypeImpl).isMoreSpecificThan(s, withDynamic)); |
} |
@override |
@@ -732,9 +714,78 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
if (relation != null) { |
return relation; |
} |
+ // This type cast is safe, because _trivialFunctionRelation checks for it. |
Leaf
2016/02/09 21:29:25
It feels like this code could become part of struc
Jennifer Messerly
2016/02/10 22:42:07
Great idea. Done.
|
+ FunctionType f2 = type as FunctionType; |
+ if (typeFormals.isNotEmpty) { |
+ if (f2.typeFormals.isEmpty) { |
+ List<DartType> dynamicTypeArgs = new List<DartType>.filled( |
Leaf
2016/02/09 21:29:25
Would be nice to use instantiateToBounds here, but
Jennifer Messerly
2016/02/10 22:42:07
Added instantiateToBounds parameter.
I do wonder
|
+ typeFormals.length, DynamicTypeImpl.instance); |
+ return instantiate(dynamicTypeArgs).isSubtypeOf(f2); |
+ } else { |
+ return structuralCompareGeneric( |
+ this, f2, (DartType t, DartType s) => t.isSubtypeOf(s)); |
+ } |
+ } else if (f2.typeFormals.isNotEmpty) { |
+ return false; |
+ } |
return structuralCompare( |
Leaf
2016/02/09 21:29:25
See comment below on the structuralCompare/structu
Jennifer Messerly
2016/02/10 22:42:07
Fixed :)
|
- this, type, (TypeImpl t, TypeImpl s) => t.isAssignableTo(s)); |
+ this, f2, (DartType t, DartType s) => t.isAssignableTo(s)); |
+ } |
+ |
+ /** |
+ * Check a [functionRelation] (typically isSubtypeOf) between [f1] and [f2] |
+ * where f1 and f2 are known to be generic function types (both have type |
+ * formals). |
+ * |
Bob Nystrom
2016/02/09 01:25:18
Writing nit: It's easy to get into the habit of fu
Jennifer Messerly
2016/02/10 22:42:07
Acknowledged.
|
+ * This will first check that f1 and f2 have the same number of type formals. |
+ * It will also check that their bounds (`<T extends LowerBound>`) are |
+ * compatible using [relation]. If the formals are not compatible, this will |
+ * return `false`. |
+ * |
+ * If [functionRelation] is omitted, this will use [relation] for both. |
+ */ |
+ static bool structuralCompareGeneric( |
Leaf
2016/02/09 21:29:25
I'm not very keen on the current factoring of this
Jennifer Messerly
2016/02/10 22:42:07
All factoring is fixed :D. At least as much as pos
|
+ FunctionType f1, FunctionType f2, bool relation(DartType t, DartType s), |
+ [bool functionRelation(FunctionType t, FunctionType s)]) { |
+ functionRelation ??= relation; |
+ |
+ List<TypeParameterElement> params1 = f1.typeFormals; |
+ List<TypeParameterElement> params2 = f2.typeFormals; |
+ int count = params1.length; |
+ if (params2.length != count) { |
+ return false; |
+ } |
+ // We build up a substitution matching up the type parameters |
+ // from the two types, {variablesFresh/variables1} and |
+ // {variablesFresh/variables2} |
+ List<DartType> variables1 = new List<DartType>(); |
+ List<DartType> variables2 = new List<DartType>(); |
+ List<DartType> variablesFresh = new List<DartType>(); |
Bob Nystrom
2016/02/09 01:25:18
<DartType>[]
Jennifer Messerly
2016/02/10 22:42:07
Done.
|
+ for (int i = 0; i < count; i++) { |
+ TypeParameterElement p1 = params1[i]; |
+ TypeParameterElement p2 = params2[i]; |
+ TypeParameterElementImpl pFresh = |
+ new TypeParameterElementImpl(p2.name, -1); |
Bob Nystrom
2016/02/09 01:25:18
What do you think about either making the offset o
Jennifer Messerly
2016/02/10 22:42:07
Done.
|
+ |
+ 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 false; |
+ } |
+ } |
+ return functionRelation( |
+ f1.instantiate(variablesFresh), f2.instantiate(variablesFresh)); |
} |
/** |