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

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

Issue 1678313002: fix part of #25200, reject non-generic function subtype of generic function (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 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 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] =

Powered by Google App Engine
This is Rietveld 408576698