Skip to content

Commit 029e06b

Browse files
kallentucommit-bot@chromium.org
authored and
commit-bot@chromium.org
committed
[analyzer] Allow downwards inference and upper bound heuristic wrt variance.
variance_downwards_inference_test now passes. The other part of this CL addresses dart-lang/language#658 The code has similar logic to https://dart-review.googlesource.com/c/sdk/+/125552. Change-Id: Ib2bc7e9c11574ccd9b16fe66472bba8c16dabd29 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125486 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Leaf Petersen <leafp@google.com> Commit-Queue: Kallen Tu <kallentu@google.com>
1 parent 74a57ac commit 029e06b

File tree

3 files changed

+103
-30
lines changed

3 files changed

+103
-30
lines changed

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

+55-26
Original file line numberDiff line numberDiff line change
@@ -2009,13 +2009,11 @@ class GenericInferrer {
20092009
// degradation for f-bounded type parameters.
20102010
var inferredTypes = new List<DartType>.filled(
20112011
typeFormals.length, UnknownInferredType.instance);
2012-
var _inferTypeParameter = downwardsInferPhase
2013-
? _inferTypeParameterFromContext
2014-
: _inferTypeParameterFromAll;
20152012

20162013
for (int i = 0; i < typeFormals.length; i++) {
2017-
TypeParameterElement typeParam = typeFormals[i];
2018-
2014+
// TODO (kallentu) : Clean up TypeParameterElementImpl casting once
2015+
// variance is added to the interface.
2016+
TypeParameterElementImpl typeParam = typeFormals[i];
20192017
_TypeConstraint extendsClause;
20202018
if (considerExtendsClause && typeParam.bound != null) {
20212019
extendsClause = new _TypeConstraint.fromExtends(
@@ -2024,8 +2022,12 @@ class GenericInferrer {
20242022
.substituteType(typeParam.bound));
20252023
}
20262024

2027-
inferredTypes[i] =
2028-
_inferTypeParameter(constraints[typeParam], extendsClause);
2025+
inferredTypes[i] = downwardsInferPhase || !typeParam.isLegacyCovariant
2026+
? _inferTypeParameterFromContext(
2027+
constraints[typeParam], extendsClause,
2028+
isContravariant: typeParam.variance.isContravariant)
2029+
: _inferTypeParameterFromAll(constraints[typeParam], extendsClause,
2030+
isContravariant: typeParam.variance.isContravariant);
20292031
}
20302032

20312033
// If the downwards infer phase has failed, we'll catch this in the upwards
@@ -2166,8 +2168,12 @@ class GenericInferrer {
21662168
/// * `int <: T`
21672169
///
21682170
/// ... and no upper bound. Therefore the lower bound is the best choice.
2171+
///
2172+
/// If [isContravariant] is `true`, then we are solving for a contravariant
2173+
/// type parameter which means we choose the upper bound rather than the
2174+
/// lower bound for normally covariant type parameters.
21692175
DartType _chooseTypeFromConstraints(Iterable<_TypeConstraint> constraints,
2170-
{bool toKnownType: false}) {
2176+
{bool toKnownType: false, @required bool isContravariant}) {
21712177
DartType lower = UnknownInferredType.instance;
21722178
DartType upper = UnknownInferredType.instance;
21732179
for (var constraint in constraints) {
@@ -2193,20 +2199,37 @@ class GenericInferrer {
21932199
// Prefer the known bound, if any.
21942200
// Otherwise take whatever bound has partial information, e.g. `Iterable<?>`
21952201
//
2196-
// For both of those, prefer the lower bound (arbitrary heuristic).
2197-
if (UnknownInferredType.isKnown(lower)) {
2198-
return lower;
2199-
}
2200-
if (UnknownInferredType.isKnown(upper)) {
2202+
// For both of those, prefer the lower bound (arbitrary heuristic) or upper
2203+
// bound if [isContravariant] is `true`
2204+
if (isContravariant) {
2205+
if (UnknownInferredType.isKnown(upper)) {
2206+
return upper;
2207+
}
2208+
if (UnknownInferredType.isKnown(lower)) {
2209+
return lower;
2210+
}
2211+
if (!identical(UnknownInferredType.instance, upper)) {
2212+
return toKnownType ? _typeSystem.upperBoundForType(upper) : upper;
2213+
}
2214+
if (!identical(UnknownInferredType.instance, lower)) {
2215+
return toKnownType ? _typeSystem.lowerBoundForType(lower) : lower;
2216+
}
22012217
return upper;
2218+
} else {
2219+
if (UnknownInferredType.isKnown(lower)) {
2220+
return lower;
2221+
}
2222+
if (UnknownInferredType.isKnown(upper)) {
2223+
return upper;
2224+
}
2225+
if (!identical(UnknownInferredType.instance, lower)) {
2226+
return toKnownType ? _typeSystem.lowerBoundForType(lower) : lower;
2227+
}
2228+
if (!identical(UnknownInferredType.instance, upper)) {
2229+
return toKnownType ? _typeSystem.upperBoundForType(upper) : upper;
2230+
}
2231+
return lower;
22022232
}
2203-
if (!identical(UnknownInferredType.instance, lower)) {
2204-
return toKnownType ? _typeSystem.lowerBoundForType(lower) : lower;
2205-
}
2206-
if (!identical(UnknownInferredType.instance, upper)) {
2207-
return toKnownType ? _typeSystem.upperBoundForType(upper) : upper;
2208-
}
2209-
return lower;
22102233
}
22112234

22122235
String _formatError(TypeParameterElement typeParam, DartType inferred,
@@ -2280,11 +2303,13 @@ class GenericInferrer {
22802303
}
22812304

22822305
DartType _inferTypeParameterFromAll(
2283-
List<_TypeConstraint> constraints, _TypeConstraint extendsClause) {
2306+
List<_TypeConstraint> constraints, _TypeConstraint extendsClause,
2307+
{@required bool isContravariant}) {
22842308
// See if we already fixed this type from downwards inference.
22852309
// If so, then we aren't allowed to change it based on argument types.
22862310
DartType t = _inferTypeParameterFromContext(
2287-
constraints.where((c) => c.isDownwards), extendsClause);
2311+
constraints.where((c) => c.isDownwards), extendsClause,
2312+
isContravariant: isContravariant);
22882313
if (UnknownInferredType.isKnown(t)) {
22892314
// Remove constraints that aren't downward ones; we'll ignore these for
22902315
// error reporting, because inference already succeeded.
@@ -2296,13 +2321,16 @@ class GenericInferrer {
22962321
constraints = constraints.toList()..add(extendsClause);
22972322
}
22982323

2299-
var choice = _chooseTypeFromConstraints(constraints, toKnownType: true);
2324+
var choice = _chooseTypeFromConstraints(constraints,
2325+
toKnownType: true, isContravariant: isContravariant);
23002326
return choice;
23012327
}
23022328

23032329
DartType _inferTypeParameterFromContext(
2304-
Iterable<_TypeConstraint> constraints, _TypeConstraint extendsClause) {
2305-
DartType t = _chooseTypeFromConstraints(constraints);
2330+
Iterable<_TypeConstraint> constraints, _TypeConstraint extendsClause,
2331+
{@required bool isContravariant}) {
2332+
DartType t = _chooseTypeFromConstraints(constraints,
2333+
isContravariant: isContravariant);
23062334
if (UnknownInferredType.isUnknown(t)) {
23072335
return t;
23082336
}
@@ -2316,7 +2344,8 @@ class GenericInferrer {
23162344
// If we consider the `T extends num` we conclude `<num>`, which works.
23172345
if (extendsClause != null) {
23182346
constraints = constraints.toList()..add(extendsClause);
2319-
return _chooseTypeFromConstraints(constraints);
2347+
return _chooseTypeFromConstraints(constraints,
2348+
isContravariant: isContravariant);
23202349
}
23212350
return t;
23222351
}

pkg/analyzer/test/generated/type_system_test.dart

+44
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,50 @@ class GenericFunctionInferenceTest extends AbstractTypeSystemTest {
11911191
expect(_inferCall(f, [intType]), [intType]);
11921192
}
11931193

1194+
void test_parameter_contravariantUseUpperBound() {
1195+
// <T>(T x, void Function(T) y) -> T
1196+
// Generates constraints int <: T <: num.
1197+
// Since T is contravariant, choose num.
1198+
var T = typeParameter('T', variance: Variance.contravariant);
1199+
var tFunction = functionTypeStar(
1200+
parameters: [requiredParameter(type: typeParameterTypeStar(T))],
1201+
returnType: voidType);
1202+
var numFunction = functionTypeStar(
1203+
parameters: [requiredParameter(type: numType)], returnType: voidType);
1204+
var function = functionTypeStar(
1205+
typeFormals: [T],
1206+
parameters: [
1207+
requiredParameter(type: typeParameterTypeStar(T)),
1208+
requiredParameter(type: tFunction)
1209+
],
1210+
returnType: typeParameterTypeStar(T),
1211+
);
1212+
1213+
expect(_inferCall(function, [intType, numFunction]), [numType]);
1214+
}
1215+
1216+
void test_parameter_covariantUseLowerBound() {
1217+
// <T>(T x, void Function(T) y) -> T
1218+
// Generates constraints int <: T <: num.
1219+
// Since T is covariant, choose int.
1220+
var T = typeParameter('T', variance: Variance.covariant);
1221+
var tFunction = functionTypeStar(
1222+
parameters: [requiredParameter(type: typeParameterTypeStar(T))],
1223+
returnType: voidType);
1224+
var numFunction = functionTypeStar(
1225+
parameters: [requiredParameter(type: numType)], returnType: voidType);
1226+
var function = functionTypeStar(
1227+
typeFormals: [T],
1228+
parameters: [
1229+
requiredParameter(type: typeParameterTypeStar(T)),
1230+
requiredParameter(type: tFunction)
1231+
],
1232+
returnType: typeParameterTypeStar(T),
1233+
);
1234+
1235+
expect(_inferCall(function, [intType, numFunction]), [intType]);
1236+
}
1237+
11941238
void test_returnFunctionWithGenericParameter() {
11951239
// <T>(T -> T) -> (T -> void)
11961240
var T = typeParameter('T');

tests/language_2/variance/variance_in_inference_error_test.dart

+4-4
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@ main() {
8787
// Since T is contravariant, we choose Upper as the solution.
8888
var inferredContraUpper = inferContraBound(ContraBound(Lower(), (Upper x) {}));
8989
lower = inferredContraUpper;
90-
// ^
91-
// [analyzer] unspecified
90+
// ^^^^^^^^^^^^^^^^^^^
91+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
9292
// [cfe] A value of type 'Exactly<Upper>' can't be assigned to a variable of type 'Exactly<Lower>'.
9393

9494
// Inference for Contrabound(...) produces Lower <: T <: Middle.
9595
// Since T is contravariant, we choose Middle as the solution.
9696
var inferredContraMiddle = inferContraBound(ContraBound(Lower(), (Middle x) {}));
9797
lower = inferredContraMiddle;
98-
// ^
99-
// [analyzer] unspecified
98+
// ^^^^^^^^^^^^^^^^^^^^
99+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
100100
// [cfe] A value of type 'Exactly<Middle>' can't be assigned to a variable of type 'Exactly<Lower>'.
101101
}

0 commit comments

Comments
 (0)