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 16e0817f8943c8acd93f69453fda4f14d9367f7f..0bf7227cd044bd3add59015902b80ed1699327da 100644 |
| --- a/pkg/analyzer/lib/src/generated/type_system.dart |
| +++ b/pkg/analyzer/lib/src/generated/type_system.dart |
| @@ -91,7 +91,11 @@ class StrongTypeSystemImpl implements TypeSystem { |
| argumentTypes[i], correspondingParameterTypes[i]); |
| } |
| - return inferringTypeSystem._infer(fnType); |
| + // We are okay inferring some type variables and not others. |
| + // |
| + // This lets our return type be as precise as possible, which will help |
| + // make any type information resulting from it more precise. |
|
Leaf
2016/02/09 21:29:25
This is purely to make error messages better, righ
Jennifer Messerly
2016/02/10 22:42:08
Yes to all. :)
|
| + return inferringTypeSystem._infer(fnType, allowPartialSolution: true); |
| } |
| /** |
| @@ -118,61 +122,23 @@ class StrongTypeSystemImpl implements TypeSystem { |
| var inferringTypeSystem = |
| new _StrongInferenceTypeSystem(typeProvider, fnType.typeFormals); |
| - // Add constraints for each corresponding pair of parameters. |
| - var fRequired = fnType.normalParameterTypes; |
| - var cRequired = contextType.normalParameterTypes; |
| - if (cRequired.length != fRequired.length) { |
| - // If the number of required parameters differs, we can't infer from this |
| - // type (this will be a static type error). |
| + // Since we're trying to infer the instantiation, we want to ignore type |
| + // formals as we check the parameters and return type. |
|
Leaf
2016/02/09 21:29:25
I would suggest phrasing this comment differently.
Jennifer Messerly
2016/02/10 22:42:07
Hah, yes, indeed. :) It now works like that. More
|
| + if (!inferringTypeSystem._isFunctionSubtypeOfRaw(fnType, contextType)) { |
| return fnType; |
| } |
| - for (int i = 0; i < fRequired.length; i++) { |
| - inferringTypeSystem.isSubtypeOf(cRequired[i], fRequired[i]); |
| - } |
| - var fOptional = fnType.optionalParameterTypes; |
| - var cOptional = contextType.optionalParameterTypes; |
| - if (cOptional.length > fOptional.length) { |
| - // If we have more optional parameters that can be passed, we can't infer |
| - // from this type (this will be a static type error). |
| - return fnType; |
| - } |
| - // Ignore any extra optional arguments in F. We only need to pass arguments |
| - // that could be passed to C. |
| - for (int i = 0; i < cOptional.length; i++) { |
| - inferringTypeSystem.isSubtypeOf(cOptional[i], fOptional[i]); |
| - } |
| + // Try to infer and instantiate the resulting type. |
| + var resultType = |
| + inferringTypeSystem._infer(fnType, allowPartialSolution: false); |
| - var fNamed = fnType.namedParameterTypes; |
| - var cNamed = contextType.namedParameterTypes; |
| - for (var name in cNamed.keys) { |
| - DartType fNamedType = fNamed[name]; |
| - if (fNamedType == null) { |
| - // If F does not have a named parameter needed for C, then we can't |
| - // infer from this type (this will be a static type error). |
| - return fnType; |
| - } |
| - DartType cNamedType = cNamed[name]; |
| - inferringTypeSystem.isSubtypeOf(cNamedType, fNamedType); |
| - } |
| - |
| - // Infer from the return type. F must return a subtype of what C returns. |
| - inferringTypeSystem.isSubtypeOf(fnType.returnType, contextType.returnType); |
| - |
| - // Instantiate the resulting type. |
| - var resultType = inferringTypeSystem._infer(fnType); |
| - |
| - // If the instantiation is not a subtype of our context (because some |
| - // constraints could not be solved), return the original type, so the error |
| - // is in terms of it. |
| + // If the instantiation failed (because some type variable constraints |
| + // could not be solved, in other words, we could not find a valid subtype), |
| + // then return the original type, so the error is in terms of it. |
| // |
| - // TODO(jmesserly): for performance, we could refactor this so the _infer |
|
Jennifer Messerly
2016/02/08 21:54:27
I went ahead and addressed this TODO. That's what
|
| - // call above bails out sooner, and then we can avoid this extra check. |
| - if (isSubtypeOf(resultType, contextType)) { |
| - return resultType; |
| - } else { |
| - return fnType; |
| - } |
| + // It would be safe to return a partial solution here, but the user |
| + // experience may be better if we simply do not infer in this case. |
| + return resultType ?? fnType; |
| } |
| /** |
| @@ -319,15 +285,27 @@ class StrongTypeSystemImpl implements TypeSystem { |
| */ |
| bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2, |
| {bool fuzzyArrows: true}) { |
| - if (!f1.typeFormals.isEmpty) { |
| + if (f1.typeFormals.isNotEmpty) { |
| if (f2.typeFormals.isEmpty) { |
| f1 = instantiateToBounds(f1); |
| - return _isFunctionSubtypeOf(f1, f2); |
| + return _isFunctionSubtypeOfRaw(f1, f2, fuzzyArrows: fuzzyArrows); |
|
Jennifer Messerly
2016/02/08 21:54:27
I feel like we don't want to drop the fuzzyArrows
Leaf
2016/02/09 21:29:25
I really think this code would be better of being
Jennifer Messerly
2016/02/10 22:42:08
Done as part of the Grand Refactoring :)
|
| } else { |
| return _isGenericFunctionSubtypeOf(f1, f2, fuzzyArrows: fuzzyArrows); |
| } |
| + } else if (f2.typeFormals.isNotEmpty) { |
|
Jennifer Messerly
2016/02/08 21:54:27
this was the else case that I believe was missing
Leaf
2016/02/09 21:29:25
Can you add a test for this in type_system_test.da
Jennifer Messerly
2016/02/10 22:42:08
I believe this is covered by the tests I added to
|
| + // A function without type formals cannot subtype one that has them. |
| + return false; |
| } |
| + return _isFunctionSubtypeOfRaw(f1, f2, fuzzyArrows: fuzzyArrows); |
| + } |
| + /** |
| + * Checks if f1 <: f2, ignoring type formals. This should generally not be |
| + * used directly, instead call [_isFunctionSubtypeOf]. Its main use is for |
| + * [_StrongInferenceTypeSystem]. |
| + */ |
| + bool _isFunctionSubtypeOfRaw(FunctionType f1, FunctionType f2, |
|
Leaf
2016/02/09 21:29:25
I don't really like the name of this - Raw doesn't
Jennifer Messerly
2016/02/10 22:42:08
It's gone :D
|
| + {bool fuzzyArrows: true}) { |
| return FunctionTypeImpl.structuralCompare( |
| f1, |
| f2, |
| @@ -345,43 +323,10 @@ class StrongTypeSystemImpl implements TypeSystem { |
| */ |
| bool _isGenericFunctionSubtypeOf(FunctionType f1, FunctionType f2, |
| {bool fuzzyArrows: true}) { |
| - 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>(); |
| - for (int i = 0; i < count; i++) { |
| - TypeParameterElement p1 = params1[i]; |
| - TypeParameterElement p2 = params2[i]; |
| - TypeParameterElementImpl pFresh = |
| - new TypeParameterElementImpl(p2.name, -1); |
| - |
| - 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 (!isSubtypeOf(bound2, bound1)) { |
| - return false; |
| - } |
| - } |
| - return _isFunctionSubtypeOf( |
| - f1.instantiate(variablesFresh), f2.instantiate(variablesFresh), |
| - fuzzyArrows: fuzzyArrows); |
| + |
| + return FunctionTypeImpl.structuralCompareGeneric(f1, f2, |
| + (t, s) => t.isSubtypeOf(s), |
| + (f, g) => _isFunctionSubtypeOf(f, g, fuzzyArrows: fuzzyArrows)); |
| } |
| bool _isInterfaceSubtypeOf( |
| @@ -790,7 +735,7 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { |
| /// Given the constraints that were given by calling [isSubtypeOf], find the |
| /// instantiation of the generic function that satisfies these constraints. |
| - FunctionType _infer(FunctionType fnType) { |
| + FunctionType _infer(FunctionType fnType, {bool allowPartialSolution: false}) { |
| List<TypeParameterType> fnTypeParams = |
| TypeParameterTypeImpl.getTypes(fnType.typeFormals); |
| @@ -832,13 +777,22 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { |
| inferredTypes[i] = |
| inferredTypes[i].substitute2(inferredTypes, fnTypeParams); |
| - // See if this actually worked. |
| - // If not, fall back to the known upper bound (if any) or `dynamic`. |
| + // See if the constraints on the type variable are satisfied. |
| + // |
| + // If not, bail out of the analysis, unless a partial solution was |
| + // requested. If we are willing to accept a partial solution, fall back to |
| + // the known upper bound (if any) or `dynamic` for this unsolvable type |
| + // variable. |
| if (inferredTypes[i].isBottom || |
| !isSubtypeOf(inferredTypes[i], |
| bound.upper.substitute2(inferredTypes, fnTypeParams)) || |
| !isSubtypeOf(bound.lower.substitute2(inferredTypes, fnTypeParams), |
| inferredTypes[i])) { |
| + // Unless a partial solution was requested, bail. |
| + if (!allowPartialSolution) { |
| + return null; |
| + } |
| + |
| inferredTypes[i] = DynamicTypeImpl.instance; |
| if (typeParam.element.bound != null) { |
| inferredTypes[i] = |