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 d3bb0647cbef46e3839ca6f230747946fe43f844..dc1fec6e525f50b7b9fc52c5445383e53d12a5ed 100644 |
--- a/pkg/analyzer/lib/src/generated/type_system.dart |
+++ b/pkg/analyzer/lib/src/generated/type_system.dart |
@@ -236,12 +236,7 @@ class StrongTypeSystemImpl extends TypeSystem { |
// subtypes (or supertypes) as necessary, and track the constraints that |
// are implied by this. |
var inferrer = new _GenericInferrer(typeProvider, this, fnType.typeFormals); |
- |
- // 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)); |
- inferrer.constrainReturnType(inferFnType, contextType); |
+ inferrer.constrainGenericFunctionInContext(fnType, contextType); |
// Infer and instantiate the resulting type. |
return inferrer.infer(fnType, fnType.typeFormals, |
@@ -1603,6 +1598,19 @@ class _GenericInferrer { |
_matchSubtypeOf(declaredType, contextType, null, origin, covariant: true); |
} |
+ /// Constrain a universal function type [fnType] used in a context |
+ /// [contextType]. |
+ void constrainGenericFunctionInContext( |
+ FunctionType fnType, DartType contextType) { |
+ var origin = new _TypeConstraintFromFunctionContext(fnType, contextType); |
+ |
+ // 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)); |
+ _matchSubtypeOf(inferFnType, contextType, null, origin, covariant: true); |
+ } |
+ |
/// Apply an argument constraint, which asserts that the [argument] staticType |
/// is a subtype of the [parameterType]. |
void constrainArgument( |
@@ -1851,20 +1859,31 @@ class _GenericInferrer { |
var constraints = _constraints[typeParam.element]; |
var typeParamBound = |
typeParam.bound.substitute2(inferredTypes, fnTypeParams); |
- if (!typeParamBound.isDynamic) { |
- constraints |
- .add(new _TypeConstraint.fromExtends(typeParam, typeParamBound)); |
- } |
+ |
var inferred = inferredTypes[i]; |
- if (constraints.any((c) => !c.isSatisifedBy(_typeSystem, inferred))) { |
- // Heuristic: keep the erroneous type, it should satisfy at least some |
- // of the constraints (e.g. the return context). If we fall back to |
- // instantiateToBounds, we'll typically get more errors (e.g. because |
- // `dynamic` is the most common bound). |
- knownTypes[typeParam] = inferred; |
- errorReporter?.reportErrorForNode(StrongModeCode.COULD_NOT_INFER, |
- errorNode, [typeParam, _formatError(inferred, constraints)]); |
- } else if (UnknownInferredType.isKnown(inferred)) { |
+ bool success = |
+ constraints.every((c) => c.isSatisifedBy(_typeSystem, inferred)); |
+ if (success && !typeParamBound.isDynamic) { |
+ // If everything else succeeded, check the `extends` constraint. |
+ var extendsConstraint = |
+ new _TypeConstraint.fromExtends(typeParam, typeParamBound); |
+ constraints.add(extendsConstraint); |
+ success = extendsConstraint.isSatisifedBy(_typeSystem, inferred); |
+ } |
+ |
+ if (!success) { |
+ errorReporter?.reportErrorForNode( |
+ StrongModeCode.COULD_NOT_INFER, |
+ errorNode, |
+ [typeParam, _formatError(typeParam, inferred, constraints)]); |
+ |
+ // Heuristic: even if we failed, keep the erroneous type. |
+ // It should satisfy at least some of the constraints (e.g. the return |
+ // context). If we fall back to instantiateToBounds, we'll typically get |
+ // more errors (e.g. because `dynamic` is the most common bound). |
+ } |
+ |
+ if (UnknownInferredType.isKnown(inferred)) { |
knownTypes[typeParam] = inferred; |
} |
} |
@@ -2039,22 +2058,23 @@ class _GenericInferrer { |
return result; |
} |
- String _formatError( |
- DartType inferred, Iterable<_TypeConstraint> constraints) { |
- var intro = "Inferred type '$inferred' does not work with constraints:"; |
+ String _formatError(TypeParameterType typeParam, DartType inferred, |
+ Iterable<_TypeConstraint> constraints) { |
+ var intro = "Tried to infer '$inferred' for '$typeParam'" |
+ " which doesn't work:"; |
var constraintsByOrigin = <_TypeConstraintOrigin, List<_TypeConstraint>>{}; |
for (var c in constraints) { |
constraintsByOrigin.putIfAbsent(c.origin, () => []).add(c); |
} |
+ // Only report unique constraint origins. |
Iterable<_TypeConstraint> isSatisified(bool expected) => constraintsByOrigin |
.values |
.where((l) => |
l.every((c) => c.isSatisifedBy(_typeSystem, inferred)) == expected) |
.expand((i) => i); |
- // Only report unique constraint origins. |
String unsatisified = _formatConstraints(isSatisified(false)); |
String satisified = _formatConstraints(isSatisified(true)); |
@@ -2074,7 +2094,6 @@ class _GenericInferrer { |
.toList(); |
int prefixMax = lineParts.map((p) => p[0].length).fold(0, math.max); |
- int middleMax = lineParts.map((p) => p[1].length).fold(0, math.max); |
// Use a set to prevent identical message lines. |
// (It's not uncommon for the same constraint to show up in a few places.) |
@@ -2082,8 +2101,12 @@ class _GenericInferrer { |
var prefix = parts[0]; |
var middle = parts[1]; |
var prefixPad = ' ' * (prefixMax - prefix.length); |
- var middlePad = ' ' * (middleMax - middle.length); |
- return ' $prefix$prefixPad $middle$middlePad ${parts[2]}'.trimRight(); |
+ var middlePad = ' ' * (prefixMax); |
+ var end = ""; |
+ if (parts.length > 2) { |
+ end = '\n $middlePad ${parts[2]}'; |
+ } |
+ return ' $prefix$prefixPad $middle$end'; |
})); |
return messageLines.join('\n'); |
@@ -2121,13 +2144,13 @@ class _TypeConstraintFromArgument extends _TypeConstraintOrigin { |
// "Map value" |
prefix = "${genericType.name} $parameterName"; |
} else { |
- prefix = "Argument '$parameterName'"; |
+ prefix = "Parameter '$parameterName'"; |
} |
return [ |
prefix, |
- "inferred as '$argumentType'", |
- "must be a '$parameterType'." |
+ "declared as '$parameterType'", |
+ "but argument is '$argumentType'." |
]; |
} |
} |
@@ -2143,7 +2166,23 @@ class _TypeConstraintFromReturnType extends _TypeConstraintOrigin { |
return [ |
"Return type", |
"declared as '$declaredType'", |
- "used where a '$contextType' is required." |
+ "used where '$contextType' is required." |
+ ]; |
+ } |
+} |
+ |
+class _TypeConstraintFromFunctionContext extends _TypeConstraintOrigin { |
+ final DartType contextType; |
+ final DartType functionType; |
+ |
+ _TypeConstraintFromFunctionContext(this.functionType, this.contextType); |
+ |
+ @override |
+ formatError() { |
+ return [ |
+ "Function type", |
+ "declared as '$functionType'", |
+ "used where '$contextType' is required." |
]; |
} |
} |
@@ -2158,8 +2197,7 @@ class _TypeConstraintFromExtendsClause extends _TypeConstraintOrigin { |
formatError() { |
return [ |
"Type parameter '$typeParam'", |
- "declared to extend '$extendsType'.", |
- "" |
+ "declared to extend '$extendsType'." |
]; |
} |
} |