Skip to content

Commit 871d0b1

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Update UP for the case of extension and interface types
Change-Id: I968cc9c8ab38c944716c1a5f392e8cc4a732c1b8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330801 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 425a42e commit 871d0b1

File tree

4 files changed

+108
-81
lines changed

4 files changed

+108
-81
lines changed

pkg/front_end/lib/src/fasta/kernel/hierarchy/hierarchy_builder.dart

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,6 @@ class ClassHierarchyBuilder
141141
assert(type1 is InterfaceType || type1 is ExtensionType);
142142
assert(type2 is InterfaceType || type2 is ExtensionType);
143143

144-
// This is a workaround for the case when `Object?` should be found as the
145-
// upper bound of extension types. If the representation type of an
146-
// extension type is nullable, then `Object` is not a supertype of that
147-
// extension type, but `Object?` is.
148-
//
149-
// TODO(cstefantsova): Remove this workaround and account for extension
150-
// types with nullable representation types directly.
151-
Nullability type1NullabilityForResult = type1 is InterfaceType
152-
? type1.nullability
153-
: (type2.isPotentiallyNullable
154-
? Nullability.nullable
155-
: type2.nullability);
156-
Nullability type2NullabilityForResult = type2 is InterfaceType
157-
? type2.nullability
158-
: (type2.isPotentiallyNullable
159-
? Nullability.nullable
160-
: type2.nullability);
161-
162144
Set<ClassHierarchyNode> supertypeNodesSet1 = supertypeNodes1.toSet();
163145
List<ClassHierarchyNode> common = <ClassHierarchyNode>[];
164146

@@ -198,8 +180,13 @@ class ClassHierarchyBuilder
198180

199181
if (common.length == 1) {
200182
assert(common.single.classBuilder.cls == coreTypes.objectClass);
201-
return coreTypes.objectRawType(uniteNullabilities(
202-
type1NullabilityForResult, type2NullabilityForResult));
183+
if (type1 is ExtensionType && type1.isPotentiallyNullable ||
184+
type2 is ExtensionType && type2.isPotentiallyNullable) {
185+
return coreTypes.objectNullableRawType;
186+
} else {
187+
return coreTypes.objectRawType(
188+
uniteNullabilities(type1.nullability, type2.nullability));
189+
}
203190
}
204191
common.sort(ClassHierarchyNode.compareMaxInheritancePath);
205192

@@ -209,23 +196,28 @@ class ClassHierarchyBuilder
209196
if (type1 is InterfaceType) {
210197
return getTypeAsInstanceOf(type1, node.classBuilder.cls,
211198
isNonNullableByDefault: isNonNullableByDefault)
212-
.withDeclaredNullability(uniteNullabilities(
213-
type1NullabilityForResult, type2NullabilityForResult));
199+
.withDeclaredNullability(
200+
uniteNullabilities(type1.nullability, type2.nullability));
214201
} else {
215202
type1 as ExtensionType;
216203
return getExtensionTypeAsInstanceOfClass(type1, node.classBuilder.cls,
217204
isNonNullableByDefault: isNonNullableByDefault)!
218-
.withDeclaredNullability(uniteNullabilities(
219-
type1NullabilityForResult, type2NullabilityForResult));
205+
.withDeclaredNullability(
206+
uniteNullabilities(type1.nullability, type2.nullability));
220207
}
221208
} else {
222209
do {
223210
i++;
224211
} while (node.maxInheritancePath == common[i + 1].maxInheritancePath);
225212
}
226213
}
227-
return coreTypes.objectRawType(uniteNullabilities(
228-
type1NullabilityForResult, type2NullabilityForResult));
214+
if (type1 is ExtensionType && type1.isPotentiallyNullable ||
215+
type2 is ExtensionType && type2.isPotentiallyNullable) {
216+
return coreTypes.objectNullableRawType;
217+
} else {
218+
return coreTypes.objectRawType(
219+
uniteNullabilities(type1.nullability, type2.nullability));
220+
}
229221
}
230222

231223
@override

pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,59 @@ class TypeSchemaEnvironmentTest extends TypeSchemaEnvironmentTestBase {
15741574
type1: "E11<int>", type2: "E11<String>", upperBound: "E11<Object>");
15751575
}
15761576

1577+
void test_upper_bound_extension_and_interface_types() {
1578+
parseTestLibrary("class A<X>; class B implements A<String>;"
1579+
"extension type E1(num? it); "
1580+
"extension type E2(num it); "
1581+
"extension type E3(num? it) implements E1; "
1582+
"extension type E4(num it) implements num; "
1583+
"extension type E5<Y extends Object>(Y it) implements A<Y>;");
1584+
checkUpperBound(type1: "E1", type2: "num?", upperBound: "Object?");
1585+
checkUpperBound(type1: "E1", type2: "num", upperBound: "Object?");
1586+
checkUpperBound(type1: "E1", type2: "int?", upperBound: "Object?");
1587+
checkUpperBound(type1: "E1", type2: "int", upperBound: "Object?");
1588+
1589+
checkUpperBound(type1: "E2", type2: "num?", upperBound: "Object?");
1590+
checkUpperBound(type1: "E2", type2: "num", upperBound: "Object");
1591+
checkUpperBound(type1: "E2", type2: "int?", upperBound: "Object?");
1592+
checkUpperBound(type1: "E2", type2: "int", upperBound: "Object");
1593+
1594+
checkUpperBound(type1: "E3", type2: "num?", upperBound: "Object?");
1595+
checkUpperBound(type1: "E3", type2: "num", upperBound: "Object?");
1596+
checkUpperBound(type1: "E3", type2: "int?", upperBound: "Object?");
1597+
checkUpperBound(type1: "E3", type2: "int", upperBound: "Object?");
1598+
1599+
checkUpperBound(type1: "E4", type2: "num?", upperBound: "num?");
1600+
checkUpperBound(type1: "E4", type2: "num", upperBound: "num");
1601+
checkUpperBound(type1: "E4", type2: "int?", upperBound: "num?");
1602+
checkUpperBound(type1: "E4", type2: "int", upperBound: "num");
1603+
1604+
checkUpperBound(type1: "E4?", type2: "num?", upperBound: "num?");
1605+
checkUpperBound(type1: "E4?", type2: "num", upperBound: "num?");
1606+
checkUpperBound(type1: "E4?", type2: "int?", upperBound: "num?");
1607+
checkUpperBound(type1: "E4?", type2: "int", upperBound: "num?");
1608+
1609+
checkUpperBound(
1610+
type1: "E5<String>", type2: "A<String?>", upperBound: "A<String?>");
1611+
checkUpperBound(
1612+
type1: "E5<String>", type2: "A<String>", upperBound: "A<String>");
1613+
checkUpperBound(
1614+
type1: "E5<String>", type2: "A<Object?>", upperBound: "A<Object?>");
1615+
checkUpperBound(
1616+
type1: "E5<String>", type2: "A<Object>", upperBound: "A<Object>");
1617+
checkUpperBound(type1: "E5<String>", type2: "B", upperBound: "A<String>");
1618+
1619+
checkUpperBound(
1620+
type1: "E5<String>?", type2: "A<String?>", upperBound: "A<String?>?");
1621+
checkUpperBound(
1622+
type1: "E5<String>?", type2: "A<String>", upperBound: "A<String>?");
1623+
checkUpperBound(
1624+
type1: "E5<String>?", type2: "A<Object?>", upperBound: "A<Object?>?");
1625+
checkUpperBound(
1626+
type1: "E5<String>?", type2: "A<Object>", upperBound: "A<Object>?");
1627+
checkUpperBound(type1: "E5<String>?", type2: "B", upperBound: "A<String>?");
1628+
}
1629+
15771630
void checkUpperBound(
15781631
{required String type1,
15791632
required String type2,

pkg/kernel/lib/class_hierarchy.dart

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -890,17 +890,6 @@ class ClosedWorldClassHierarchy
890890
assert(type1 is! InterfaceType || info1 != null);
891891
assert(type2 is! InterfaceType || info2 != null);
892892

893-
Nullability type1NullabilityForResult = type1 is InterfaceType
894-
? type1.nullability
895-
: (type1.isPotentiallyNullable
896-
? Nullability.nullable
897-
: type1.nullability);
898-
Nullability type2NullabilityForResult = type2 is InterfaceType
899-
? type2.nullability
900-
: (type2.isPotentiallyNullable
901-
? Nullability.nullable
902-
: type2.nullability);
903-
904893
// Walk the lists finding their intersection, looking for a depth that has a
905894
// single candidate.
906895
int i1 = 0;
@@ -948,10 +937,8 @@ class ClosedWorldClassHierarchy
948937
// immediately. Since all interface types are subtypes of Object, this
949938
// ensures the loop terminates.
950939
if (next.classNode.typeParameters.isEmpty) {
951-
candidate = coreTypes.rawType(
952-
next.classNode,
953-
uniteNullabilities(
954-
type1NullabilityForResult, type2NullabilityForResult));
940+
candidate = coreTypes.rawType(next.classNode,
941+
uniteNullabilities(type1.nullability, type2.nullability));
955942
if (currentDepth == 0) return candidate;
956943
++numCandidatesAtThisDepth;
957944
} else {
@@ -986,8 +973,8 @@ class ClosedWorldClassHierarchy
986973
superType2 = legacyErasure(superType2) as InterfaceType;
987974
}
988975
if (superType1 == superType2) {
989-
candidate = superType1.withDeclaredNullability(uniteNullabilities(
990-
type1NullabilityForResult, type2NullabilityForResult));
976+
candidate = superType1.withDeclaredNullability(
977+
uniteNullabilities(type1.nullability, type2.nullability));
991978
++numCandidatesAtThisDepth;
992979
}
993980
}
@@ -1006,21 +993,14 @@ class ClosedWorldClassHierarchy
1006993
assert(supertypes1.isNotEmpty || type1 is ExtensionType);
1007994
assert(supertypes2.isNotEmpty || type2 is ExtensionType);
1008995

1009-
Nullability type1NullabilityForResult = type1 is InterfaceType
1010-
? type1.nullability
1011-
: (type1.isPotentiallyNullable
1012-
? Nullability.nullable
1013-
: type1.nullability);
1014-
1015-
Nullability type2NullabilityForResult = type2 is InterfaceType
1016-
? type2.nullability
1017-
: (type2.isPotentiallyNullable
1018-
? Nullability.nullable
1019-
: type2.nullability);
1020-
1021996
if (supertypes1.isEmpty || supertypes2.isEmpty) {
1022-
return coreTypes.objectRawType(uniteNullabilities(
1023-
type1NullabilityForResult, type2NullabilityForResult));
997+
if (type1 is ExtensionType && type1.isPotentiallyNullable ||
998+
type2 is ExtensionType && type2.isPotentiallyNullable) {
999+
return coreTypes.objectNullableRawType;
1000+
} else {
1001+
return coreTypes.objectRawType(
1002+
uniteNullabilities(type1.nullability, type2.nullability));
1003+
}
10241004
}
10251005

10261006
List<_ClassInfo> combinedInfos1 =
@@ -1033,7 +1013,12 @@ class ClosedWorldClassHierarchy
10331013
]);
10341014

10351015
return _getLegacyLeastUpperBoundInternal(
1036-
type1, type2, null, null, combinedInfos1, combinedInfos2,
1016+
type1,
1017+
type2,
1018+
type1 is InterfaceType ? infoFor(type1.classNode) : null,
1019+
type2 is InterfaceType ? infoFor(type2.classNode) : null,
1020+
combinedInfos1,
1021+
combinedInfos2,
10371022
isNonNullableByDefault: isNonNullableByDefault);
10381023
}
10391024

pkg/kernel/lib/src/standard_bounds.dart

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -996,28 +996,10 @@ mixin StandardBounds {
996996
assert((type1 is InterfaceType || type1 is ExtensionType) &&
997997
(type2 is InterfaceType || type2 is ExtensionType));
998998

999-
// This is a workaround for the case when `Object?` should be found as the
1000-
// upper bound of extension types. If the representation type of an
1001-
// extension type is nullable, then `Object` is not a supertype of that
1002-
// extension type, but `Object?` is.
1003-
//
1004-
// TODO(cstefantsova): Remove this workaround and account for extension
1005-
// types with nullable representation types directly.
1006-
Nullability type1NullabilityForResult = type1 is InterfaceType
1007-
? type1.nullability
1008-
: (type1.isPotentiallyNullable
1009-
? Nullability.nullable
1010-
: type1.nullability);
1011-
Nullability type2NullabilityForResult = type2 is InterfaceType
1012-
? type2.nullability
1013-
: (type2.isPotentiallyNullable
1014-
? Nullability.nullable
1015-
: type2.nullability);
1016-
1017999
if (type1 is InterfaceType && type2 is InterfaceType) {
10181000
return hierarchy.getLegacyLeastUpperBound(type1, type2,
10191001
isNonNullableByDefault: isNonNullableByDefault);
1020-
} else if (type1 is ExtensionType && type2 is ExtensionType) {
1002+
} else if (type1 is ExtensionType || type2 is ExtensionType) {
10211003
// This mimics the legacy least upper bound implementation for regular
10221004
// classes, where the least upper bound is found as the single common
10231005
// supertype with the highest class hierarchy depth.
@@ -1069,10 +1051,20 @@ mixin StandardBounds {
10691051

10701052
List<ExtensionType> supertypes1 = [];
10711053
List<InterfaceType> superInterfaceTypes1 = [];
1072-
computeSuperTypes(type1, supertypes1, superInterfaceTypes1);
1054+
if (type1 is ExtensionType) {
1055+
computeSuperTypes(type1, supertypes1, superInterfaceTypes1);
1056+
} else {
1057+
type1 as InterfaceType;
1058+
superInterfaceTypes1 = <InterfaceType>[type1];
1059+
}
10731060
List<ExtensionType> supertypes2 = [];
10741061
List<InterfaceType> superInterfaceTypes2 = [];
1075-
computeSuperTypes(type2, supertypes2, superInterfaceTypes2);
1062+
if (type2 is ExtensionType) {
1063+
computeSuperTypes(type2, supertypes2, superInterfaceTypes2);
1064+
} else {
1065+
type2 as InterfaceType;
1066+
superInterfaceTypes2 = <InterfaceType>[type2];
1067+
}
10761068

10771069
Set<ExtensionType> set = supertypes1.toSet()..retainAll(supertypes2);
10781070
Map<int, List<ExtensionType>> commonSupertypesByDepth = {};
@@ -1098,8 +1090,13 @@ mixin StandardBounds {
10981090
type1, type2, superInterfaceTypes1, superInterfaceTypes2,
10991091
isNonNullableByDefault: isNonNullableByDefault);
11001092
}
1101-
return coreTypes.objectRawType(uniteNullabilities(
1102-
type1NullabilityForResult, type2NullabilityForResult));
1093+
if (type1 is ExtensionType && type1.isPotentiallyNullable ||
1094+
type2 is ExtensionType && type2.isPotentiallyNullable) {
1095+
return coreTypes.objectNullableRawType;
1096+
} else {
1097+
return coreTypes.objectRawType(
1098+
uniteNullabilities(type1.nullability, type2.nullability));
1099+
}
11031100
}
11041101

11051102
/// Computes the nullability-aware lower bound of two function types.

0 commit comments

Comments
 (0)