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

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: fix synthetic ctor 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..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] =
« no previous file with comments | « pkg/analyzer/lib/src/generated/static_type_analyzer.dart ('k') | pkg/analyzer/test/src/task/strong/checker_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698