Skip to content

Commit 0fe54a6

Browse files
author
John Messerly
committed
fix #26990, incorrect substitution of upper bound
We were handling the "extends" clause too early, when we did not have enough information to correctly substitute it. So we tried to substitute later on, but this breaks cases where we (correctly) inferred using the argument types. The fix is to move the "extends" clause handling to a place where we have the inferred types for our preceding type variables available. [email protected] Review URL: https://codereview.chromium.org/2205993002 .
1 parent 9aee504 commit 0fe54a6

File tree

3 files changed

+80
-29
lines changed

3 files changed

+80
-29
lines changed

pkg/analyzer/lib/src/generated/type_system.dart

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class StrongTypeSystemImpl extends TypeSystem {
163163
// subtypes (or supertypes) as necessary, and track the constraints that
164164
// are implied by this.
165165
var inferringTypeSystem =
166-
new _StrongInferenceTypeSystem(typeProvider, fnType.typeFormals);
166+
new _StrongInferenceTypeSystem(typeProvider, this, fnType.typeFormals);
167167

168168
// Since we're trying to infer the instantiation, we want to ignore type
169169
// formals as we check the parameters and return type.
@@ -216,7 +216,7 @@ class StrongTypeSystemImpl extends TypeSystem {
216216
// subtypes (or supertypes) as necessary, and track the constraints that
217217
// are implied by this.
218218
var inferringTypeSystem =
219-
new _StrongInferenceTypeSystem(typeProvider, fnType.typeFormals);
219+
new _StrongInferenceTypeSystem(typeProvider, this, fnType.typeFormals);
220220

221221
// Special case inference for Future.then.
222222
//
@@ -1271,43 +1271,36 @@ class TypeSystemImpl extends TypeSystem {
12711271
/// Tracks upper and lower type bounds for a set of type parameters.
12721272
class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
12731273
final TypeProvider _typeProvider;
1274+
1275+
/// The outer strong mode type system, used for GLB and LUB, so we don't
1276+
/// recurse into our constraint solving code.
1277+
final StrongTypeSystemImpl _typeSystem;
12741278
final Map<TypeParameterType, _TypeParameterBound> _bounds;
12751279

1276-
_StrongInferenceTypeSystem(
1277-
this._typeProvider, Iterable<TypeParameterElement> typeFormals)
1278-
: _bounds =
1279-
new Map.fromIterable(typeFormals, key: (t) => t.type, value: (t) {
1280-
_TypeParameterBound bound = new _TypeParameterBound();
1281-
if (t.bound != null) bound.upper = t.bound;
1282-
return bound;
1283-
});
1280+
_StrongInferenceTypeSystem(this._typeProvider, this._typeSystem,
1281+
Iterable<TypeParameterElement> typeFormals)
1282+
: _bounds = new Map.fromIterable(typeFormals,
1283+
key: (t) => t.type, value: (t) => new _TypeParameterBound());
12841284

12851285
/// Given the constraints that were given by calling [isSubtypeOf], find the
12861286
/// instantiation of the generic function that satisfies these constraints.
12871287
FunctionType _infer(FunctionType fnType) {
12881288
List<TypeParameterType> fnTypeParams =
12891289
TypeParameterTypeImpl.getTypes(fnType.typeFormals);
12901290

1291-
var inferredTypes = new List<DartType>.from(fnTypeParams, growable: false);
1291+
// Initialize the inferred type array.
1292+
//
1293+
// They all start as `dynamic` to offer reasonable degradation for f-bounded
1294+
// type parameters.
1295+
var inferredTypes = new List<DartType>.filled(
1296+
fnTypeParams.length, DynamicTypeImpl.instance,
1297+
growable: false);
1298+
12921299
for (int i = 0; i < fnTypeParams.length; i++) {
12931300
TypeParameterType typeParam = fnTypeParams[i];
1294-
_TypeParameterBound bound = _bounds[typeParam];
12951301

1296-
// Now we've computed lower and upper bounds for each type parameter.
1302+
// Apply the `extends` clause for the type parameter, if any.
12971303
//
1298-
// To decide on which type to assign, we look at the return type and see
1299-
// if the type parameter occurs in covariant or contravariant positions.
1300-
//
1301-
// If the type is "passed in" at all, or if our lower bound was bottom,
1302-
// we choose the upper bound as being the most useful.
1303-
//
1304-
// Otherwise we choose the more precise lower bound.
1305-
_TypeParameterVariance variance =
1306-
new _TypeParameterVariance.from(typeParam, fnType.returnType);
1307-
1308-
inferredTypes[i] =
1309-
variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
1310-
13111304
// Assumption: if the current type parameter has an "extends" clause
13121305
// that refers to another type variable we are inferring, it will appear
13131306
// before us or in this list position. For example:
@@ -1323,8 +1316,28 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
13231316
// Or if the type parameter's bound depends on itself such as:
13241317
//
13251318
// <T extends Clonable<T>>
1319+
DartType declaredUpperBound = typeParam.element.bound;
1320+
if (declaredUpperBound != null) {
1321+
// Assert that the type parameter is a subtype of its bound.
1322+
_inferTypeParameterSubtypeOf(typeParam,
1323+
declaredUpperBound.substitute2(inferredTypes, fnTypeParams), null);
1324+
}
1325+
1326+
// Now we've computed lower and upper bounds for each type parameter.
1327+
//
1328+
// To decide on which type to assign, we look at the return type and see
1329+
// if the type parameter occurs in covariant or contravariant positions.
1330+
//
1331+
// If the type is "passed in" at all, or if our lower bound was bottom,
1332+
// we choose the upper bound as being the most useful.
1333+
//
1334+
// Otherwise we choose the more precise lower bound.
1335+
_TypeParameterVariance variance =
1336+
new _TypeParameterVariance.from(typeParam, fnType.returnType);
1337+
1338+
_TypeParameterBound bound = _bounds[typeParam];
13261339
inferredTypes[i] =
1327-
inferredTypes[i].substitute2(inferredTypes, fnTypeParams);
1340+
variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
13281341

13291342
// See if the constraints on the type variable are satisfied.
13301343
//
@@ -1358,7 +1371,8 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
13581371
// We already know T1 <: U, for some U.
13591372
// So update U to reflect the new constraint T1 <: GLB(U, T2)
13601373
//
1361-
bound.upper = getGreatestLowerBound(_typeProvider, bound.upper, t2);
1374+
bound.upper =
1375+
_typeSystem.getGreatestLowerBound(_typeProvider, bound.upper, t2);
13621376
// Optimistically assume we will be able to satisfy the constraint.
13631377
return true;
13641378
}
@@ -1372,7 +1386,8 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
13721386
// We already know L <: T2, for some L.
13731387
// So update L to reflect the new constraint LUB(L, T1) <: T2
13741388
//
1375-
bound.lower = getLeastUpperBound(_typeProvider, bound.lower, t1);
1389+
bound.lower =
1390+
_typeSystem.getLeastUpperBound(_typeProvider, bound.lower, t1);
13761391
// Optimistically assume we will be able to satisfy the constraint.
13771392
return true;
13781393
}

pkg/analyzer/test/src/summary/resynthesize_ast_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ class AstInferredTypeTest extends AbstractResynthesizeTest
155155
super.test_circularReference_viaClosures_initializerTypes();
156156
}
157157

158+
@override
159+
@failingTest
160+
void test_constructors_inferenceFBounded() {
161+
super.test_constructors_inferenceFBounded();
162+
}
163+
158164
@override
159165
@failingTest
160166
void test_constructors_inferFromArguments() {

pkg/analyzer/test/src/task/strong/inferred_type_test.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ class C2 implements A, B {
588588
''');
589589
}
590590

591+
void test_constructors_inferenceFBounded() {
592+
// Regression for https://github.com/dart-lang/sdk/issues/26990
593+
var unit = checkFile('''
594+
class Clonable<T> {}
595+
596+
class Pair<T extends Clonable<T>, U extends Clonable<U>> {
597+
T t;
598+
U u;
599+
Pair(this.t, this.u);
600+
Pair._();
601+
Pair<U, T> get reversed => /*info:INFERRED_TYPE_ALLOCATION*/new Pair(u, t);
602+
}
603+
final x = /*info:INFERRED_TYPE_ALLOCATION*/new Pair._();
604+
''');
605+
var x = unit.topLevelVariables[0];
606+
expect(x.type.toString(), 'Pair<Clonable<dynamic>, Clonable<dynamic>>');
607+
}
608+
591609
void test_constructors_inferFromArguments() {
592610
var unit = checkFile('''
593611
class C<T> {
@@ -734,6 +752,18 @@ main() {
734752
expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
735753
}
736754

755+
void test_constructors_reverseTypeParameters() {
756+
// Regression for https://github.com/dart-lang/sdk/issues/26990
757+
checkFile('''
758+
class Pair<T, U> {
759+
T t;
760+
U u;
761+
Pair(this.t, this.u);
762+
Pair<U, T> get reversed => /*info:INFERRED_TYPE_ALLOCATION*/new Pair(u, t);
763+
}
764+
''');
765+
}
766+
737767
void test_doNotInferOverriddenFieldsThatExplicitlySayDynamic_infer() {
738768
checkFile('''
739769
class A {

0 commit comments

Comments
 (0)