Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit eb271d9

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Update NNBD_TOP_MERGE for pending updates to the spec.
These changes are not yet in the spec, but it seems that we want them before we can start using NNBD_TOP_MERGE for merging interfaces. Change-Id: I4ba0f95b169febd05a2cbc1a50edf342227ee217 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132971 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1 parent d6762df commit eb271d9

File tree

5 files changed

+150
-27
lines changed

5 files changed

+150
-27
lines changed

pkg/analyzer/lib/src/dart/element/top_merge.dart

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ import 'package:analyzer/dart/element/type.dart';
88
import 'package:analyzer/src/dart/element/element.dart';
99
import 'package:analyzer/src/dart/element/type.dart';
1010
import 'package:analyzer/src/dart/element/type_algebra.dart';
11+
import 'package:analyzer/src/generated/type_system.dart';
1112

1213
class TopMergeHelper {
14+
final TypeSystemImpl typeSystem;
15+
16+
TopMergeHelper(this.typeSystem);
17+
1318
/**
1419
* Merges two types into a single type.
1520
* Compute the canonical representation of [T].
@@ -18,7 +23,7 @@ class TopMergeHelper {
1823
* See `accepted/future-releases/nnbd/feature-specification.md`
1924
* See `#classes-defined-in-opted-in-libraries`
2025
*/
21-
static DartType topMerge(DartType T, DartType S) {
26+
DartType topMerge(DartType T, DartType S) {
2227
var T_nullability = T.nullabilitySuffix;
2328
var S_nullability = S.nullabilitySuffix;
2429

@@ -51,6 +56,16 @@ class TopMergeHelper {
5156
return VoidTypeImpl.instance;
5257
}
5358

59+
// NNBD_TOP_MERGE(void, Object*) = void
60+
// NNBD_TOP_MERGE(Object*, void) = void
61+
var T_isObjectStar =
62+
T_nullability == NullabilitySuffix.star && T.isDartCoreObject;
63+
var S_isObjectStar =
64+
S_nullability == NullabilitySuffix.star && S.isDartCoreObject;
65+
if (T_isVoid && S_isObjectStar || T_isObjectStar && S_isVoid) {
66+
return VoidTypeImpl.instance;
67+
}
68+
5469
// NNBD_TOP_MERGE(void, dynamic) = void
5570
// NNBD_TOP_MERGE(dynamic, void) = void
5671
if (T_isVoid && S_isDynamic || T_isDynamic && S_isVoid) {
@@ -66,6 +81,12 @@ class TopMergeHelper {
6681
return S;
6782
}
6883

84+
// NNBD_TOP_MERGE(Object*, dynamic) = Object?
85+
// NNBD_TOP_MERGE(dynamic, Object*) = Object?
86+
if (T_isObjectStar && S_isDynamic || T_isDynamic && S_isObjectStar) {
87+
return typeSystem.objectQuestion;
88+
}
89+
6990
// NNBD_TOP_MERGE(Never*, Null) = Null
7091
// NNBD_TOP_MERGE(Null, Never*) = Null
7192
if (identical(T, NeverTypeImpl.instanceLegacy) &&
@@ -144,7 +165,7 @@ class TopMergeHelper {
144165
throw _TopMergeStateError(T, S, 'Unexpected pair');
145166
}
146167

147-
static FunctionTypeImpl _functionTypes(FunctionType T, FunctionType S) {
168+
FunctionTypeImpl _functionTypes(FunctionType T, FunctionType S) {
148169
var T_typeParameters = T.typeFormals;
149170
var S_typeParameters = S.typeFormals;
150171
if (T_typeParameters.length != S_typeParameters.length) {
@@ -205,12 +226,39 @@ class TopMergeHelper {
205226
throw _TopMergeStateError(T, S, 'Different named parameter names');
206227
}
207228

208-
var R_type = mergeTypes(T_parameter.type, S_parameter.type);
229+
DartType R_type;
230+
231+
// Given two corresponding parameters of type `T1` and `T2`, where at least
232+
// one of the parameters is covariant:
233+
var T_isCovariant = T_parameter.isCovariant;
234+
var S_isCovariant = S_parameter.isCovariant;
235+
var R_isCovariant = T_isCovariant || S_isCovariant;
236+
if (R_isCovariant) {
237+
var T1 = T_parameter.type;
238+
var T2 = S_parameter.type;
239+
var T1_isSubtype = typeSystem.isSubtypeOf(T1, T2);
240+
var T2_isSubtype = typeSystem.isSubtypeOf(T2, T1);
241+
if (T1_isSubtype && T2_isSubtype) {
242+
// if `T1 <: T2` and `T2 <: T1`, then the result is
243+
// `NNBD_TOP_MERGE(T1, T2)`, and it is covariant.
244+
R_type = mergeTypes(T_parameter.type, S_parameter.type);
245+
} else if (T1_isSubtype) {
246+
// otherwise, if `T1 <: T2`, then the result is
247+
// `T2` and it is covariant.
248+
R_type = T2;
249+
} else {
250+
// otherwise, the result is `T1` and it is covariant.
251+
R_type = T1;
252+
}
253+
} else {
254+
R_type = mergeTypes(T_parameter.type, S_parameter.type);
255+
}
256+
209257
R_parameters[i] = ParameterElementImpl.synthetic(
210258
T_parameter.name,
211259
R_type,
212260
T_kind,
213-
);
261+
)..isExplicitlyCovariant = R_isCovariant;
214262
}
215263

216264
return FunctionTypeImpl(
@@ -221,7 +269,7 @@ class TopMergeHelper {
221269
);
222270
}
223271

224-
static InterfaceType _interfaceTypes(InterfaceType T, InterfaceType S) {
272+
InterfaceType _interfaceTypes(InterfaceType T, InterfaceType S) {
225273
if (T.element != S.element) {
226274
throw _TopMergeStateError(T, S, 'Different class elements');
227275
}
@@ -242,7 +290,7 @@ class TopMergeHelper {
242290
}
243291
}
244292

245-
static _MergeTypeParametersResult _typeParameters(
293+
_MergeTypeParametersResult _typeParameters(
246294
List<TypeParameterElement> aParameters,
247295
List<TypeParameterElement> bParameters,
248296
) {

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

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ class Dart2TypeSystem extends TypeSystem {
9797
@required this.typeProvider,
9898
}) : super(isNonNullableByDefault: isNonNullableByDefault);
9999

100+
InterfaceTypeImpl get objectNone =>
101+
_objectNoneCached ??= (typeProvider.objectType as TypeImpl)
102+
.withNullability(NullabilitySuffix.none);
103+
104+
InterfaceTypeImpl get objectQuestion =>
105+
_objectQuestionCached ??= (typeProvider.objectType as TypeImpl)
106+
.withNullability(NullabilitySuffix.question);
107+
100108
InterfaceType get _interfaceTypeFunctionNone {
101109
return typeProvider.functionType.element.instantiate(
102110
typeArguments: const [],
@@ -108,14 +116,6 @@ class Dart2TypeSystem extends TypeSystem {
108116
_nullNoneCached ??= (typeProvider.nullType as TypeImpl)
109117
.withNullability(NullabilitySuffix.none);
110118

111-
InterfaceTypeImpl get _objectNone =>
112-
_objectNoneCached ??= (typeProvider.objectType as TypeImpl)
113-
.withNullability(NullabilitySuffix.none);
114-
115-
InterfaceTypeImpl get _objectQuestion =>
116-
_objectQuestionCached ??= (typeProvider.objectType as TypeImpl)
117-
.withNullability(NullabilitySuffix.question);
118-
119119
/// Returns true iff the type [t] accepts function types, and requires an
120120
/// implicit coercion if interface types with a `call` method are passed in.
121121
///
@@ -580,7 +580,7 @@ class Dart2TypeSystem extends TypeSystem {
580580
// UP(T Function<...>(...), T2) = Object
581581
// UP(T1, T Function<...>(...)) = Object
582582
if (T1 is FunctionType || T2 is FunctionType) {
583-
return _objectNone;
583+
return objectNone;
584584
}
585585

586586
// UP(T1, T2) = T2 if T1 <: T2
@@ -1230,7 +1230,7 @@ class Dart2TypeSystem extends TypeSystem {
12301230
// then `T0 <: T1` if `Object? <: T1`.
12311231
if (identical(T0, DynamicTypeImpl.instance) ||
12321232
identical(T0, VoidTypeImpl.instance)) {
1233-
if (isSubtypeOf(_objectQuestion, T1)) {
1233+
if (isSubtypeOf(objectQuestion, T1)) {
12341234
return true;
12351235
}
12361236
}
@@ -1250,8 +1250,8 @@ class Dart2TypeSystem extends TypeSystem {
12501250
// then `T0 <: T1`iff `S <: Object`.
12511251
if (T0_nullability == NullabilitySuffix.none &&
12521252
T0 is TypeParameterTypeImpl) {
1253-
var bound = T0.element.bound ?? _objectQuestion;
1254-
return isSubtypeOf(bound, _objectNone);
1253+
var bound = T0.element.bound ?? objectQuestion;
1254+
return isSubtypeOf(bound, objectNone);
12551255
}
12561256
// * if `T0` is `FutureOr<S>` for some `S`,
12571257
// then `T0 <: T1` iff `S <: Object`
@@ -1345,8 +1345,8 @@ class Dart2TypeSystem extends TypeSystem {
13451345
// * `T0 <: T1` iff `T0 <: X1` and `T0 <: S1`
13461346
if (T0 is TypeParameterTypeImpl) {
13471347
if (T1 is TypeParameterTypeImpl && T0.definition == T1.definition) {
1348-
var S0 = T0.element.bound ?? _objectQuestion;
1349-
var S1 = T1.element.bound ?? _objectQuestion;
1348+
var S0 = T0.element.bound ?? objectQuestion;
1349+
var S1 = T1.element.bound ?? objectQuestion;
13501350
if (isSubtypeOf(S0, S1)) {
13511351
return true;
13521352
}
@@ -1379,7 +1379,7 @@ class Dart2TypeSystem extends TypeSystem {
13791379
// * or `T0` is `X0` and `X0` has bound `S0` and `S0 <: T1`
13801380
// * or `T0` is `X0 & S0` and `S0 <: T1`
13811381
if (T0 is TypeParameterTypeImpl) {
1382-
var S0 = T0.element.bound ?? _objectQuestion;
1382+
var S0 = T0.element.bound ?? objectQuestion;
13831383
if (isSubtypeOf(S0, T1)) {
13841384
return true;
13851385
}
@@ -1403,7 +1403,7 @@ class Dart2TypeSystem extends TypeSystem {
14031403
// or `T0` is `X0` and `X0` has bound `S0` and `S0 <: T1`
14041404
// or `T0` is `X0 & S0` and `S0 <: T1`
14051405
if (T0 is TypeParameterTypeImpl) {
1406-
var S0 = T0.element.bound ?? _objectQuestion;
1406+
var S0 = T0.element.bound ?? objectQuestion;
14071407
return isSubtypeOf(S0, T1);
14081408
}
14091409
// iff
@@ -1422,7 +1422,7 @@ class Dart2TypeSystem extends TypeSystem {
14221422
// Left Type Variable Bound: `T0` is a type variable `X0` with bound `B0`
14231423
// * and `B0 <: T1`
14241424
if (T0 is TypeParameterTypeImpl) {
1425-
var S0 = T0.element.bound ?? _objectQuestion;
1425+
var S0 = T0.element.bound ?? objectQuestion;
14261426
if (isSubtypeOf(S0, T1)) {
14271427
return true;
14281428
}
@@ -1706,7 +1706,7 @@ class Dart2TypeSystem extends TypeSystem {
17061706
normalize(e.type),
17071707
// ignore: deprecated_member_use_from_same_package
17081708
e.parameterKind,
1709-
);
1709+
)..isExplicitlyCovariant = e.isCovariant;
17101710
}).toList(),
17111711
returnType: normalize(functionType.returnType),
17121712
nullabilitySuffix: NullabilitySuffix.none,
@@ -1762,7 +1762,7 @@ class Dart2TypeSystem extends TypeSystem {
17621762
* See `#classes-defined-in-opted-in-libraries`
17631763
*/
17641764
DartType topMerge(DartType T, DartType S) {
1765-
return TopMergeHelper.topMerge(T, S);
1765+
return TopMergeHelper(this).topMerge(T, S);
17661766
}
17671767

17681768
@override

pkg/analyzer/test/generated/elements_types_mixin.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,27 +425,36 @@ mixin ElementsTypesMixin {
425425
ParameterElement namedParameter({
426426
@required String name,
427427
@required DartType type,
428+
bool isCovariant = false,
428429
}) {
429430
var parameter = ParameterElementImpl(name, 0);
430431
parameter.parameterKind = ParameterKind.NAMED;
431432
parameter.type = type;
433+
parameter.isExplicitlyCovariant = isCovariant;
432434
return parameter;
433435
}
434436

435437
ParameterElement namedRequiredParameter({
436438
@required String name,
437439
@required DartType type,
440+
bool isCovariant = false,
438441
}) {
439442
var parameter = ParameterElementImpl(name, 0);
440443
parameter.parameterKind = ParameterKind.NAMED_REQUIRED;
441444
parameter.type = type;
445+
parameter.isExplicitlyCovariant = isCovariant;
442446
return parameter;
443447
}
444448

445-
ParameterElement positionalParameter({String name, @required DartType type}) {
449+
ParameterElement positionalParameter({
450+
String name,
451+
@required DartType type,
452+
bool isCovariant = false,
453+
}) {
446454
var parameter = ParameterElementImpl(name ?? '', 0);
447455
parameter.parameterKind = ParameterKind.POSITIONAL;
448456
parameter.type = type;
457+
parameter.isExplicitlyCovariant = isCovariant;
449458
return parameter;
450459
}
451460

@@ -457,10 +466,15 @@ mixin ElementsTypesMixin {
457466
return TypeParameterMember(element, null, bound);
458467
}
459468

460-
ParameterElement requiredParameter({String name, @required DartType type}) {
469+
ParameterElement requiredParameter({
470+
String name,
471+
@required DartType type,
472+
bool isCovariant = false,
473+
}) {
461474
var parameter = ParameterElementImpl(name ?? '', 0);
462475
parameter.parameterKind = ParameterKind.REQUIRED;
463476
parameter.type = type;
477+
parameter.isExplicitlyCovariant = isCovariant;
464478
return parameter;
465479
}
466480

pkg/analyzer/test/src/dart/element/top_merge_test.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,50 @@ class TopMergeTest extends _Base {
7171
);
7272
}
7373

74+
test_function_covariant() {
75+
_check(
76+
functionTypeNone(
77+
returnType: voidNone,
78+
parameters: [
79+
requiredParameter(type: objectQuestion, isCovariant: true),
80+
],
81+
),
82+
functionTypeNone(
83+
returnType: voidNone,
84+
parameters: [
85+
requiredParameter(type: dynamicNone),
86+
],
87+
),
88+
functionTypeNone(
89+
returnType: voidNone,
90+
parameters: [
91+
requiredParameter(type: objectQuestion, isCovariant: true),
92+
],
93+
),
94+
);
95+
96+
_check(
97+
functionTypeNone(
98+
returnType: voidNone,
99+
parameters: [
100+
requiredParameter(type: intNone, isCovariant: true),
101+
],
102+
),
103+
functionTypeNone(
104+
returnType: voidNone,
105+
parameters: [
106+
requiredParameter(type: numNone),
107+
],
108+
),
109+
functionTypeNone(
110+
returnType: voidNone,
111+
parameters: [
112+
requiredParameter(type: numNone, isCovariant: true),
113+
],
114+
),
115+
);
116+
}
117+
74118
test_function_parameters_mismatch() {
75119
_check(
76120
functionTypeNone(
@@ -225,6 +269,16 @@ class TopMergeTest extends _Base {
225269
_check(dynamicNone, objectQuestion, objectQuestion);
226270
}
227271

272+
test_objectStar() {
273+
// NNBD_TOP_MERGE(Object*, void) = void
274+
// NNBD_TOP_MERGE(void, Object*) = void
275+
_check(objectStar, voidNone, voidNone);
276+
277+
// NNBD_TOP_MERGE(Object*, dynamic) = Object?
278+
// NNBD_TOP_MERGE(dynamic, Object*) = Object?
279+
_check(objectStar, dynamicNone, objectQuestion);
280+
}
281+
228282
test_typeParameter() {
229283
var T = typeParameter('T');
230284

pkg/analyzer/test/src/dart/resolution/resolution.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ mixin ResolutionTest implements ResourceProviderMixin {
162162
expect(actual, isNull);
163163
}
164164

165+
void assertElementString(Element element, String expected) {
166+
var str = element.getDisplayString(
167+
withNullability: typeToStringWithNullability,
168+
);
169+
expect(str, expected);
170+
}
171+
165172
void assertElementTypes(List<DartType> types, List<DartType> expected,
166173
{bool ordered = false}) {
167174
if (ordered) {

0 commit comments

Comments
 (0)