Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1402)

Unified Diff: pkg/analyzer/lib/src/generated/type_system.dart

Issue 1644403005: fix #25619 - downward inference on generic function tear-offs (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 cff5601b570d77c988699669cade20d1367e204e..fb48bbe9d03b141b033d5e76e0d0ed33fc2b4295 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -34,9 +34,6 @@ class StrongTypeSystemImpl implements TypeSystem {
@override
bool canPromoteToType(DartType to, DartType from) => isSubtypeOf(to, from);
- @override
- bool isMoreSpecificThan(DartType t1, DartType t2) => isSubtypeOf(t1, t2);
-
/**
* Given a type t, if t is an interface type with a call method
* defined, return the function type for the call method, otherwise
@@ -74,22 +71,19 @@ class StrongTypeSystemImpl implements TypeSystem {
/// L is the lower bound of that type parameter.
FunctionType inferCallFromArguments(
TypeProvider typeProvider,
- FunctionTypeImpl fnType,
+ FunctionType fnType,
List<DartType> correspondingParameterTypes,
List<DartType> argumentTypes) {
if (fnType.typeFormals.isEmpty) {
return fnType;
}
- List<TypeParameterType> fnTypeParams =
- TypeParameterTypeImpl.getTypes(fnType.typeFormals);
-
// Create a TypeSystem that will allow certain type parameters to be
// inferred. It will optimistically assume these type parameters can be
// subtypes (or supertypes) as necessary, and track the constraints that
// are implied by this.
var inferringTypeSystem =
- new _StrongInferenceTypeSystem(typeProvider, fnTypeParams);
+ new _StrongInferenceTypeSystem(typeProvider, fnType.typeFormals);
for (int i = 0; i < argumentTypes.length; i++) {
// Try to pass each argument to each parameter, recording any type
@@ -98,61 +92,88 @@ class StrongTypeSystemImpl implements TypeSystem {
argumentTypes[i], correspondingParameterTypes[i]);
}
- var inferredTypes = new List<DartType>.from(fnTypeParams, growable: false);
- for (int i = 0; i < fnTypeParams.length; i++) {
- TypeParameterType typeParam = fnTypeParams[i];
- _TypeParameterBound bound = inferringTypeSystem._bounds[typeParam];
+ return inferringTypeSystem._infer(fnType);
+ }
- // Now we've computed lower and upper bounds for each type parameter.
- //
- // To decide on which type to assign, we look at the return type and see
- // if the type parameter occurs in covariant or contravariant positions.
- //
- // If the type is "passed in" at all, or if our lower bound was bottom,
- // we choose the upper bound as being the most useful.
- //
- // Otherwise we choose the more precise lower bound.
- _TypeParameterVariance variance =
- new _TypeParameterVariance.from(typeParam, fnType.returnType);
+ /**
+ * Given a generic function type `F<T0, T1, ... Tn>` and a context type C,
+ * infer an instantiation of F, such that `F<S0, S1, ..., Sn>` <: C.
+ *
+ * This is similar to [inferCallFromArguments], but the return type is also
+ * considered as part of the solution.
+ *
+ * If this function is called with a [contextType] that is also
+ * uninstantiated, or a [fnType] that is already instantiated, it will have
+ * no effect and return [fnType].
+ */
+ FunctionType inferFunctionTypeInstantiation(TypeProvider typeProvider,
+ FunctionType contextType, FunctionType fnType) {
+ if (contextType.typeFormals.isNotEmpty || fnType.typeFormals.isEmpty) {
+ return fnType;
+ }
- inferredTypes[i] =
- variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
+ // Create a TypeSystem that will allow certain type parameters to be
+ // inferred. It will optimistically assume these type parameters can be
+ // subtypes (or supertypes) as necessary, and track the constraints that
+ // are implied by this.
+ 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).
+ return fnType;
+ }
+ for (int i = 0; i < fRequired.length; i++) {
+ inferringTypeSystem.isSubtypeOf(cRequired[i], fRequired[i]);
+ }
- // Assumption: if the current type parameter has an "extends" clause
- // that refers to another type variable we are inferring, it will appear
- // before us or in this list position. For example:
- //
- // <TFrom, TTo extends TFrom>
- //
- // We may infer TTo is TFrom. In that case, we already know what TFrom
- // is inferred as, so we can substitute it now. This also handles more
- // complex cases such as:
- //
- // <TFrom, TTo extends Iterable<TFrom>>
- //
- // Or if the type parameter's bound depends on itself such as:
- //
- // <T extends Clonable<T>>
- inferredTypes[i] =
- inferredTypes[i].substitute2(inferredTypes, fnTypeParams);
+ 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]);
+ }
- // See if this actually worked.
- // If not, fall back to the known upper bound (if any) or `dynamic`.
- if (inferredTypes[i].isBottom ||
- !isSubtypeOf(inferredTypes[i],
- bound.upper.substitute2(inferredTypes, fnTypeParams)) ||
- !isSubtypeOf(bound.lower.substitute2(inferredTypes, fnTypeParams),
- inferredTypes[i])) {
- inferredTypes[i] = DynamicTypeImpl.instance;
- if (typeParam.element.bound != null) {
- inferredTypes[i] =
- typeParam.element.bound.substitute2(inferredTypes, fnTypeParams);
- }
+ 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);
}
- // Return the instantiated type.
- return fnType.instantiate(inferredTypes);
+ // 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.
+ //
+ // 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;
+ }
}
/**
@@ -250,6 +271,9 @@ class StrongTypeSystemImpl implements TypeSystem {
}
@override
+ bool isMoreSpecificThan(DartType t1, DartType t2) => isSubtypeOf(t1, t2);
+
+ @override
bool isSubtypeOf(DartType leftType, DartType rightType) {
return _isSubtypeOf(leftType, rightType, null);
}
@@ -582,14 +606,6 @@ abstract class TypeSystem {
bool canPromoteToType(DartType to, DartType from);
/**
- * Return `true` if the [leftType] is more specific than the [rightType]
- * (that is, if leftType << rightType), as defined in the Dart language spec.
- *
- * In strong mode, this is equivalent to [isSubtypeOf].
- */
- bool isMoreSpecificThan(DartType leftType, DartType rightType);
-
- /**
* Compute the least upper bound of two types.
*/
DartType getLeastUpperBound(
@@ -611,6 +627,14 @@ abstract class TypeSystem {
bool isAssignableTo(DartType leftType, DartType rightType);
/**
+ * Return `true` if the [leftType] is more specific than the [rightType]
+ * (that is, if leftType << rightType), as defined in the Dart language spec.
+ *
+ * In strong mode, this is equivalent to [isSubtypeOf].
+ */
+ bool isMoreSpecificThan(DartType leftType, DartType rightType);
+
+ /**
* Return `true` if the [leftType] is a subtype of the [rightType] (that is,
* if leftType <: rightType).
*/
@@ -633,10 +657,6 @@ class TypeSystemImpl implements TypeSystem {
TypeSystemImpl();
@override
- bool isMoreSpecificThan(DartType t1, DartType t2) =>
- t1.isMoreSpecificThan(t2);
-
- @override
bool canPromoteToType(DartType to, DartType from) {
// Declared type should not be "dynamic".
// Promoted type should not be "dynamic".
@@ -720,6 +740,32 @@ class TypeSystemImpl implements TypeSystem {
}
/**
+ * Instantiate the function type using `dynamic` for all generic parameters.
+ */
+ FunctionType instantiateToBounds(FunctionType function) {
+ int count = function.typeFormals.length;
+ if (count == 0) {
+ return function;
+ }
+ return function.instantiate(
+ new List<DartType>.filled(count, DynamicTypeImpl.instance));
+ }
+
+ @override
+ bool isAssignableTo(DartType leftType, DartType rightType) {
+ return leftType.isAssignableTo(rightType);
+ }
+
+ @override
+ bool isMoreSpecificThan(DartType t1, DartType t2) =>
+ t1.isMoreSpecificThan(t2);
+
+ @override
+ bool isSubtypeOf(DartType leftType, DartType rightType) {
+ return leftType.isSubtypeOf(rightType);
+ }
+
+ /**
* Compute the least upper bound of function types [f] and [g].
*
* The spec rules for LUB on function types, informally, are pretty simple
@@ -767,7 +813,7 @@ class TypeSystemImpl implements TypeSystem {
// Ignore any extra optional positional parameters if one has more than the
// other.
- int length = math.min(fPositional.length, gPositional.length);
+ int length = math.min(fPositional.length, gPositional.length);
for (int i = 0; i < length; i++) {
parameters.add(new ParameterElementImpl.synthetic(
fPositionalNames[i],
@@ -796,28 +842,6 @@ class TypeSystemImpl implements TypeSystem {
function.type = new FunctionTypeImpl(function);
return function.type;
}
-
- /**
- * Instantiate the function type using `dynamic` for all generic parameters.
- */
- FunctionType instantiateToBounds(FunctionType function) {
- int count = function.typeFormals.length;
- if (count == 0) {
- return function;
- }
- return function.instantiate(
- new List<DartType>.filled(count, DynamicTypeImpl.instance));
- }
-
- @override
- bool isAssignableTo(DartType leftType, DartType rightType) {
- return leftType.isAssignableTo(rightType);
- }
-
- @override
- bool isSubtypeOf(DartType leftType, DartType rightType) {
- return leftType.isSubtypeOf(rightType);
- }
}
/// Tracks upper and lower type bounds for a set of type parameters.
@@ -826,13 +850,77 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
final Map<TypeParameterType, _TypeParameterBound> _bounds;
_StrongInferenceTypeSystem(
- this._typeProvider, Iterable<TypeParameterType> typeParams)
- : _bounds = new Map.fromIterable(typeParams, value: (t) {
+ this._typeProvider, Iterable<TypeParameterElement> typeFormals)
+ : _bounds =
+ new Map.fromIterable(typeFormals, key: (t) => t.type, value: (t) {
_TypeParameterBound bound = new _TypeParameterBound();
- if (t.element.bound != null) bound.upper = t.element.bound;
+ if (t.bound != null) bound.upper = t.bound;
return bound;
});
+ /// Given the constraints that were given by calling [isSubtypeOf], find the
+ /// instantiation of the generic function that satisfies these constraints.
+ FunctionType _infer(FunctionType fnType) {
+ List<TypeParameterType> fnTypeParams =
+ TypeParameterTypeImpl.getTypes(fnType.typeFormals);
+
+ var inferredTypes = new List<DartType>.from(fnTypeParams, growable: false);
+ for (int i = 0; i < fnTypeParams.length; i++) {
+ TypeParameterType typeParam = fnTypeParams[i];
+ _TypeParameterBound bound = _bounds[typeParam];
+
+ // Now we've computed lower and upper bounds for each type parameter.
+ //
+ // To decide on which type to assign, we look at the return type and see
+ // if the type parameter occurs in covariant or contravariant positions.
+ //
+ // If the type is "passed in" at all, or if our lower bound was bottom,
+ // we choose the upper bound as being the most useful.
+ //
+ // Otherwise we choose the more precise lower bound.
+ _TypeParameterVariance variance =
+ new _TypeParameterVariance.from(typeParam, fnType.returnType);
+
+ inferredTypes[i] =
+ variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
+
+ // Assumption: if the current type parameter has an "extends" clause
+ // that refers to another type variable we are inferring, it will appear
+ // before us or in this list position. For example:
+ //
+ // <TFrom, TTo extends TFrom>
+ //
+ // We may infer TTo is TFrom. In that case, we already know what TFrom
+ // is inferred as, so we can substitute it now. This also handles more
+ // complex cases such as:
+ //
+ // <TFrom, TTo extends Iterable<TFrom>>
+ //
+ // Or if the type parameter's bound depends on itself such as:
+ //
+ // <T extends Clonable<T>>
+ 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`.
+ if (inferredTypes[i].isBottom ||
+ !isSubtypeOf(inferredTypes[i],
+ bound.upper.substitute2(inferredTypes, fnTypeParams)) ||
+ !isSubtypeOf(bound.lower.substitute2(inferredTypes, fnTypeParams),
+ inferredTypes[i])) {
+ inferredTypes[i] = DynamicTypeImpl.instance;
+ if (typeParam.element.bound != null) {
+ inferredTypes[i] =
+ typeParam.element.bound.substitute2(inferredTypes, fnTypeParams);
+ }
+ }
+ }
+
+ // Return the instantiated type.
+ return fnType.instantiate(inferredTypes);
+ }
+
@override
bool _inferTypeParameterSubtypeOf(
DartType t1, DartType t2, Set<Element> visited) {
« no previous file with comments | « pkg/analyzer/lib/src/generated/static_type_analyzer.dart ('k') | pkg/analyzer/test/src/task/strong/inferred_type_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698