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..3dead28efa4ea727c1252102e8f8538ff07607e1 100644 |
--- a/pkg/analyzer/lib/src/generated/type_system.dart |
+++ b/pkg/analyzer/lib/src/generated/type_system.dart |
@@ -53,20 +53,20 @@ class StrongTypeSystemImpl implements TypeSystem { |
} |
/// Given a function type with generic type parameters, infer the type |
- /// parameters from the actual argument types, and return it. If we can't. |
- /// returns the original function type. |
+ /// parameters from the actual argument types, and return the instantiated |
+ /// function type. If we can't, returns the original function type. |
/// |
/// Concretely, given a function type with parameter types P0, P1, ... Pn, |
/// result type R, and generic type parameters T0, T1, ... Tm, use the |
/// argument types A0, A1, ... An to solve for the type parameters. |
/// |
/// For each parameter Pi, we want to ensure that Ai <: Pi. We can do this by |
- /// running the subtype algorithm, and when we reach a type parameter Pj, |
+ /// running the subtype algorithm, and when we reach a type parameter Tj, |
/// recording the lower or upper bound it must satisfy. At the end, all |
/// constraints can be combined to determine the type. |
/// |
/// As a simplification, we do not actually store all constraints on each type |
- /// parameter Pj. Instead we track Uj and Lj where U is the upper bound and |
+ /// parameter Tj. Instead we track Uj and Lj where U is the upper bound and |
/// L is the lower bound of that type parameter. |
FunctionType inferCallFromArguments( |
TypeProvider typeProvider, |
@@ -91,7 +91,14 @@ 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. |
+ // |
+ // This is simply a heuristic: the code is incorrect, and we'll issue an |
+ // error on this call, to indicate that types don't match. |
+ return inferringTypeSystem._infer(fnType, allowPartialSolution: true); |
} |
/** |
@@ -118,61 +125,25 @@ 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. |
+ var inferFnType = |
+ fnType.instantiate(TypeParameterTypeImpl.getTypes(fnType.typeFormals)); |
+ if (!inferringTypeSystem.isSubtypeOf(inferFnType, 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]); |
- } |
- |
- 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); |
+ // Try to infer and instantiate the resulting type. |
+ var resultType = |
+ inferringTypeSystem._infer(fnType, allowPartialSolution: false); |
- // 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 |
- // 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,69 +290,14 @@ class StrongTypeSystemImpl implements TypeSystem { |
*/ |
bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2, |
{bool fuzzyArrows: true}) { |
- if (!f1.typeFormals.isEmpty) { |
- if (f2.typeFormals.isEmpty) { |
- f1 = instantiateToBounds(f1); |
- return _isFunctionSubtypeOf(f1, f2); |
- } else { |
- return _isGenericFunctionSubtypeOf(f1, f2, fuzzyArrows: fuzzyArrows); |
- } |
- } |
- return FunctionTypeImpl.structuralCompare( |
+ return FunctionTypeImpl.relate( |
f1, |
f2, |
(DartType t1, DartType t2) => |
_isSubtypeOf(t2, t1, null, dynamicIsBottom: fuzzyArrows), |
- isSubtypeOf); |
- } |
- |
- /** |
- * Check that [f1] is a subtype of [f2] where f1 and f2 are known |
- * to be generic function types (both have type parameters) |
- * [fuzzyArrows] indicates whether or not the f1 and f2 should be |
- * treated as fuzzy arrow types (and hence dynamic parameters to f2 treated |
- * as bottom). |
- */ |
- 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); |
+ instantiateToBounds, |
+ returnRelation: isSubtypeOf); |
} |
bool _isInterfaceSubtypeOf( |
@@ -790,7 +706,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 +748,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] = |