Skip to content

Commit d5d8896

Browse files
kallentucommit-bot@chromium.org
authored andcommitted
[analyzer] Added variance support for upper/lower bounds.
Change-Id: I64f44a7068c6caa222787ccb7dfbb3ed8b5fd0fe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124420 Commit-Queue: Kallen Tu <kallentu@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Leaf Petersen <leafp@google.com>
1 parent a65aff1 commit d5d8896

File tree

3 files changed

+198
-31
lines changed

3 files changed

+198
-31
lines changed

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

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,13 +2131,17 @@ class InterfaceLeastUpperBoundHelper {
21312131
/// causing pain in real code. The current algorithm is:
21322132
/// 1. If either of the types is a supertype of the other, return it.
21332133
/// This is in fact the best result in this case.
2134-
/// 2. If the two types have the same class element, then take the
2135-
/// pointwise least upper bound of the type arguments. This is again
2136-
/// the best result, except that the recursive calls may not return
2137-
/// the true least upper bounds. The result is guaranteed to be a
2138-
/// well-formed type under the assumption that the input types were
2139-
/// well-formed (and assuming that the recursive calls return
2140-
/// well-formed types).
2134+
/// 2. If the two types have the same class element and are implicitly or
2135+
/// explicitly covariant, then take the pointwise least upper bound of
2136+
/// the type arguments. This is again the best result, except that the
2137+
/// recursive calls may not return the true least upper bounds. The
2138+
/// result is guaranteed to be a well-formed type under the assumption
2139+
/// that the input types were well-formed (and assuming that the
2140+
/// recursive calls return well-formed types).
2141+
/// If the variance of the type parameter is contravariant, we take the
2142+
/// greatest lower bound of the type arguments. If the variance of the
2143+
/// type parameter is invariant, we verify if the type arguments satisfy
2144+
/// subtyping in both directions, then choose a bound.
21412145
/// 3. Otherwise return the spec-defined least upper bound. This will
21422146
/// be an upper bound, might (or might not) be least, and might
21432147
/// (or might not) be a well-formed type.
@@ -2163,11 +2167,42 @@ class InterfaceLeastUpperBoundHelper {
21632167
if (type1.element == type2.element) {
21642168
var args1 = type1.typeArguments;
21652169
var args2 = type2.typeArguments;
2166-
2170+
var params = type1.element.typeParameters;
21672171
assert(args1.length == args2.length);
2172+
assert(args1.length == params.length);
2173+
21682174
var args = List<DartType>(args1.length);
21692175
for (int i = 0; i < args1.length; i++) {
2170-
args[i] = typeSystem.getLeastUpperBound(args1[i], args2[i]);
2176+
// TODO (kallentu) : Clean up TypeParameterElementImpl checks and
2177+
// casting once variance is added to the interface.
2178+
TypeParameterElement parameter = params[i];
2179+
Variance parameterVariance = Variance.covariant;
2180+
if (parameter is TypeParameterElementImpl) {
2181+
parameterVariance = parameter.variance;
2182+
}
2183+
2184+
if (parameterVariance.isContravariant) {
2185+
if (typeSystem is Dart2TypeSystem) {
2186+
args[i] = (typeSystem as Dart2TypeSystem)
2187+
.getGreatestLowerBound(args1[i], args2[i]);
2188+
} else {
2189+
args[i] = typeSystem.getLeastUpperBound(args1[i], args2[i]);
2190+
}
2191+
} else if (parameterVariance.isInvariant) {
2192+
if (!typeSystem.isSubtypeOf(args1[i], args2[i]) ||
2193+
!typeSystem.isSubtypeOf(args2[i], args1[i])) {
2194+
// No bound will be valid, find bound at the interface level.
2195+
return _computeLeastUpperBound(
2196+
InstantiatedClass.of(type1),
2197+
InstantiatedClass.of(type2),
2198+
).withNullability(nullability);
2199+
}
2200+
// TODO (kallentu) : Fix asymmetric bounds behavior for invariant type
2201+
// parameters.
2202+
args[i] = args1[i];
2203+
} else {
2204+
args[i] = typeSystem.getLeastUpperBound(args1[i], args2[i]);
2205+
}
21712206
}
21722207

21732208
return new InterfaceTypeImpl.explicit(

pkg/analyzer/test/generated/type_system_test.dart

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:analyzer/error/listener.dart';
1414
import 'package:analyzer/src/dart/ast/token.dart' show KeywordToken;
1515
import 'package:analyzer/src/dart/element/member.dart';
1616
import 'package:analyzer/src/dart/element/type.dart';
17+
import 'package:analyzer/src/dart/resolver/variance.dart';
1718
import 'package:analyzer/src/error/codes.dart';
1819
import 'package:analyzer/src/generated/engine.dart';
1920
import 'package:analyzer/src/generated/resolver.dart';
@@ -1948,6 +1949,137 @@ class GreatestLowerBoundTest extends BoundTestBase {
19481949
_checkGreatestLowerBound(listOfIntType, listOfIntType, listOfIntType);
19491950
}
19501951

1952+
void test_typeParameters_covariant_same() {
1953+
// class A<out T>
1954+
var T = typeParameter('T', variance: Variance.covariant);
1955+
var A = class_(name: 'A', typeParameters: [T]);
1956+
1957+
// A<num>
1958+
var aNum = interfaceType(A, typeArguments: [numType]);
1959+
1960+
_checkLeastUpperBound(aNum, aNum, aNum);
1961+
}
1962+
1963+
void test_typeParameters_covariant_different() {
1964+
// class A<out T>
1965+
var T = typeParameter('T', variance: Variance.covariant);
1966+
var A = class_(name: 'A', typeParameters: [T]);
1967+
1968+
// A<num>
1969+
// A<int>
1970+
var aNum = interfaceType(A, typeArguments: [numType]);
1971+
var aInt = interfaceType(A, typeArguments: [intType]);
1972+
1973+
_checkLeastUpperBound(aInt, aNum, aNum);
1974+
}
1975+
1976+
void test_typeParameters_contravariant_same() {
1977+
// class A<in T>
1978+
var T = typeParameter('T', variance: Variance.contravariant);
1979+
var A = class_(name: 'A', typeParameters: [T]);
1980+
1981+
// A<num>
1982+
var aNum = interfaceType(A, typeArguments: [numType]);
1983+
1984+
_checkLeastUpperBound(aNum, aNum, aNum);
1985+
}
1986+
1987+
void test_typeParameters_contravariant_different() {
1988+
// class A<in T>
1989+
var T = typeParameter('T', variance: Variance.contravariant);
1990+
var A = class_(name: 'A', typeParameters: [T]);
1991+
1992+
// A<num>
1993+
// A<int>
1994+
var aNum = interfaceType(A, typeArguments: [numType]);
1995+
var aInt = interfaceType(A, typeArguments: [intType]);
1996+
1997+
_checkLeastUpperBound(aInt, aNum, aInt);
1998+
}
1999+
2000+
void test_typeParameters_invariant_same() {
2001+
// class A<inout T>
2002+
var T = typeParameter('T', variance: Variance.invariant);
2003+
var A = class_(name: 'A', typeParameters: [T]);
2004+
2005+
// A<num>
2006+
var aNum = interfaceType(A, typeArguments: [numType]);
2007+
2008+
_checkLeastUpperBound(aNum, aNum, aNum);
2009+
}
2010+
2011+
void test_typeParameters_invariant_object() {
2012+
// class A<inout T>
2013+
var T = typeParameter('T', variance: Variance.invariant);
2014+
var A = class_(name: 'A', typeParameters: [T]);
2015+
2016+
// A<num>
2017+
// A<int>
2018+
var aNum = interfaceType(A, typeArguments: [numType]);
2019+
var aInt = interfaceType(A, typeArguments: [intType]);
2020+
2021+
_checkLeastUpperBound(aNum, aInt, objectType);
2022+
}
2023+
2024+
void test_typeParameters_multi_basic() {
2025+
// class Multi<out T, inout U, in V>
2026+
var T = typeParameter('T', variance: Variance.covariant);
2027+
var U = typeParameter('T', variance: Variance.invariant);
2028+
var V = typeParameter('T', variance: Variance.contravariant);
2029+
var Multi = class_(name: 'A', typeParameters: [T, U, V]);
2030+
2031+
// Multi<num, num, num>
2032+
// Multi<int, num, int>
2033+
var multiNumNumNum =
2034+
interfaceType(Multi, typeArguments: [numType, numType, numType]);
2035+
var multiIntNumInt =
2036+
interfaceType(Multi, typeArguments: [intType, numType, intType]);
2037+
2038+
// We expect Multi<num, num, int>
2039+
var multiNumNumInt =
2040+
interfaceType(Multi, typeArguments: [numType, numType, intType]);
2041+
2042+
_checkLeastUpperBound(multiNumNumNum, multiIntNumInt, multiNumNumInt);
2043+
}
2044+
2045+
void test_typeParameters_multi_objectInterface() {
2046+
// class Multi<out T, inout U, in V>
2047+
var T = typeParameter('T', variance: Variance.covariant);
2048+
var U = typeParameter('T', variance: Variance.invariant);
2049+
var V = typeParameter('T', variance: Variance.contravariant);
2050+
var Multi = class_(name: 'A', typeParameters: [T, U, V]);
2051+
2052+
// Multi<num, String, num>
2053+
// Multi<int, num, int>
2054+
var multiNumStringNum =
2055+
interfaceType(Multi, typeArguments: [numType, stringType, numType]);
2056+
var multiIntNumInt =
2057+
interfaceType(Multi, typeArguments: [intType, numType, intType]);
2058+
2059+
_checkLeastUpperBound(multiNumStringNum, multiIntNumInt, objectType);
2060+
}
2061+
2062+
void test_typeParameters_multi_objectType() {
2063+
// class Multi<out T, inout U, in V>
2064+
var T = typeParameter('T', variance: Variance.covariant);
2065+
var U = typeParameter('T', variance: Variance.invariant);
2066+
var V = typeParameter('T', variance: Variance.contravariant);
2067+
var Multi = class_(name: 'A', typeParameters: [T, U, V]);
2068+
2069+
// Multi<String, num, num>
2070+
// Multi<int, num, int>
2071+
var multiStringNumNum =
2072+
interfaceType(Multi, typeArguments: [stringType, numType, numType]);
2073+
var multiIntNumInt =
2074+
interfaceType(Multi, typeArguments: [intType, numType, intType]);
2075+
2076+
// We expect Multi<Object, num, int>
2077+
var multiObjectNumInt =
2078+
interfaceType(Multi, typeArguments: [objectType, numType, intType]);
2079+
2080+
_checkLeastUpperBound(multiStringNumNum, multiIntNumInt, multiObjectNumInt);
2081+
}
2082+
19512083
void test_unrelatedClasses() {
19522084
// class A
19532085
// class B

tests/language_2/variance/variance_upper_lower_bounds_error_test.dart

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,76 +27,76 @@ main() {
2727
var contraLowerActual =
2828
exactly(condition ? Contravariant<Upper>() : Contravariant<Lower>());
2929
Exactly<Contravariant<Upper>> contraUpperExpected = contraLowerActual;
30-
// ^
31-
// [analyzer] unspecified
30+
// ^^^^^^^^^^^^^^^^^
31+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
3232
// [cfe] A value of type 'Exactly<Contravariant<Lower>>' can't be assigned to a variable of type 'Exactly<Contravariant<Upper>>'.
3333

3434
var contraMiddleActual =
3535
exactly(condition ? Contravariant<Upper>() : Contravariant<Middle>());
3636
contraUpperExpected = contraMiddleActual;
37-
// ^
38-
// [analyzer] unspecified
37+
// ^^^^^^^^^^^^^^^^^^
38+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
3939
// [cfe] A value of type 'Exactly<Contravariant<Middle>>' can't be assigned to a variable of type 'Exactly<Contravariant<Upper>>'.
4040

4141
var covMiddleActual =
4242
exactly(condition ? Covariant<Middle>() : Covariant<Lower>());
4343
Exactly<Covariant<Lower>> covLowerExpected = covMiddleActual;
44-
// ^
45-
// [analyzer] unspecified
44+
// ^^^^^^^^^^^^^^^
45+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
4646
// [cfe] A value of type 'Exactly<Covariant<Middle>>' can't be assigned to a variable of type 'Exactly<Covariant<Lower>>'.
4747

4848
var covUpperActual =
4949
exactly(condition ? Covariant<Upper>() : Covariant<Lower>());
5050
covLowerExpected = covUpperActual;
51-
// ^
52-
// [analyzer] unspecified
51+
// ^^^^^^^^^^^^^^
52+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
5353
// [cfe] A value of type 'Exactly<Covariant<Upper>>' can't be assigned to a variable of type 'Exactly<Covariant<Lower>>'.
5454

5555
var invObjectActual =
5656
exactly(condition ? Invariant<Upper>() : Invariant<Middle>());
5757
Exactly<Invariant<Middle>> invMiddleExpected = invObjectActual;
58-
// ^
59-
// [analyzer] unspecified
58+
// ^^^^^^^^^^^^^^^
59+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
6060
// [cfe] A value of type 'Exactly<Object>' can't be assigned to a variable of type 'Exactly<Invariant<Middle>>'.
6161
Exactly<Invariant<Upper>> invUpperExpected = invObjectActual;
62-
// ^
63-
// [analyzer] unspecified
62+
// ^^^^^^^^^^^^^^^
63+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
6464
// [cfe] A value of type 'Exactly<Object>' can't be assigned to a variable of type 'Exactly<Invariant<Upper>>'.
6565

6666
var legacyCovMiddleActual =
6767
exactly(condition ? LegacyCovariant<Middle>() : LegacyCovariant<Lower>());
6868
Exactly<LegacyCovariant<Lower>> legacyCovLowerExpected =
6969
legacyCovMiddleActual;
70-
// ^
71-
// [analyzer] unspecified
70+
// ^^^^^^^^^^^^^^^^^^^^^
71+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
7272
// [cfe] A value of type 'Exactly<LegacyCovariant<Middle>>' can't be assigned to a variable of type 'Exactly<LegacyCovariant<Lower>>'.
7373

7474
var legacyCovUpperActual =
7575
exactly(condition ? LegacyCovariant<Upper>() : LegacyCovariant<Lower>());
7676
legacyCovLowerExpected = legacyCovUpperActual;
77-
// ^
78-
// [analyzer] unspecified
77+
// ^^^^^^^^^^^^^^^^^^^^
78+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
7979
// [cfe] A value of type 'Exactly<LegacyCovariant<Upper>>' can't be assigned to a variable of type 'Exactly<LegacyCovariant<Lower>>'.
8080

8181
var multiActual = exactly(condition
8282
? Multi<Middle, Middle, Middle>()
8383
: Multi<Lower, Middle, Lower>());
8484
Exactly<Multi<Lower, Middle, Lower>> multiExpected = multiActual;
85-
// ^
86-
// [analyzer] unspecified
85+
// ^^^^^^^^^^^
86+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
8787
// [cfe] A value of type 'Exactly<Multi<Middle, Middle, Lower>>' can't be assigned to a variable of type 'Exactly<Multi<Lower, Middle, Lower>>'.
8888

8989
var multiActual2 = exactly(
9090
condition ? Multi<Middle, int, Middle>() : Multi<Lower, Middle, Lower>());
9191
Exactly<Multi<Middle, Object, Lower>> multiObjectExpected = multiActual2;
92-
// ^
93-
// [analyzer] unspecified
92+
// ^^^^^^^^^^^^
93+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
9494
// [cfe] A value of type 'Exactly<Object>' can't be assigned to a variable of type 'Exactly<Multi<Middle, Object, Lower>>'.
9595

9696
var multiActual3 = exactly(
9797
condition ? Multi<int, Middle, Middle>() : Multi<Lower, Middle, Lower>());
9898
Exactly<Object> multiObjectExpected2 = multiActual3;
99-
// ^
100-
// [analyzer] unspecified
99+
// ^^^^^^^^^^^^
100+
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
101101
// [cfe] A value of type 'Exactly<Multi<Object, Middle, Lower>>' can't be assigned to a variable of type 'Exactly<Object>'.
102102
}

0 commit comments

Comments
 (0)