@@ -132,69 +132,145 @@ class ClassHierarchyBuilder
132
132
return getClassAsInstanceOf (subtype, superclass) != null ;
133
133
}
134
134
135
- @override
136
- InterfaceType getLegacyLeastUpperBound (
137
- InterfaceType type1, InterfaceType type2,
135
+ InterfaceType _getLegacyLeastUpperBoundInternal (
136
+ /* InterfaceType | ExtensionType */ DartType type1,
137
+ /* InterfaceType | ExtensionType */ DartType type2,
138
+ List <ClassHierarchyNode > supertypeNodes1,
139
+ List <ClassHierarchyNode > supertypeNodes2,
138
140
{required bool isNonNullableByDefault}) {
139
- if (type1 == type2) return type1;
141
+ assert (type1 is InterfaceType || type1 is ExtensionType );
142
+ assert (type2 is InterfaceType || type2 is ExtensionType );
140
143
141
- // LLUB(Null, List<dynamic>*) works differently for opt-in and opt-out
142
- // libraries. In opt-out libraries the legacy behavior is preserved, so
143
- // LLUB(Null, List<dynamic>*) = List<dynamic>*. In opt-out libraries the
144
- // rules imply that LLUB(Null, List<dynamic>*) = List<dynamic>?.
145
- if (! isNonNullableByDefault) {
146
- if (type1 is NullType ) {
147
- return type2;
148
- }
149
- if (type2 is NullType ) {
150
- return type1;
151
- }
152
- }
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);
153
161
154
- ClassHierarchyNode node1 = getNodeFromClass (type1.classNode);
155
- ClassHierarchyNode node2 = getNodeFromClass (type2.classNode);
156
- Set <ClassHierarchyNode > nodes1 = node1.computeAllSuperNodes (this ).toSet ();
157
- List <ClassHierarchyNode > nodes2 = node2.computeAllSuperNodes (this );
162
+ Set <ClassHierarchyNode > supertypeNodesSet1 = supertypeNodes1.toSet ();
158
163
List <ClassHierarchyNode > common = < ClassHierarchyNode > [];
159
164
160
- for (int i = 0 ; i < nodes2 .length; i++ ) {
161
- ClassHierarchyNode node = nodes2 [i];
165
+ for (int i = 0 ; i < supertypeNodes2 .length; i++ ) {
166
+ ClassHierarchyNode node = supertypeNodes2 [i];
162
167
if (node.classBuilder.cls.isAnonymousMixin) {
163
168
// Never find unnamed mixin application in least upper bound.
164
169
continue ;
165
170
}
166
- if (nodes1.contains (node)) {
167
- DartType candidate1 = getTypeAsInstanceOf (type1, node.classBuilder.cls,
168
- isNonNullableByDefault: isNonNullableByDefault);
169
- DartType candidate2 = getTypeAsInstanceOf (type2, node.classBuilder.cls,
170
- isNonNullableByDefault: isNonNullableByDefault);
171
+ if (supertypeNodesSet1.contains (node)) {
172
+ DartType candidate1;
173
+ if (type1 is InterfaceType ) {
174
+ candidate1 = getTypeAsInstanceOf (type1, node.classBuilder.cls,
175
+ isNonNullableByDefault: isNonNullableByDefault);
176
+ } else {
177
+ type1 as ExtensionType ;
178
+ candidate1 = getExtensionTypeAsInstanceOfClass (
179
+ type1, node.classBuilder.cls,
180
+ isNonNullableByDefault: isNonNullableByDefault)! ;
181
+ }
182
+
183
+ DartType candidate2;
184
+ if (type2 is InterfaceType ) {
185
+ candidate2 = getTypeAsInstanceOf (type2, node.classBuilder.cls,
186
+ isNonNullableByDefault: isNonNullableByDefault);
187
+ } else {
188
+ type2 as ExtensionType ;
189
+ candidate2 = getExtensionTypeAsInstanceOfClass (
190
+ type2, node.classBuilder.cls,
191
+ isNonNullableByDefault: isNonNullableByDefault)! ;
192
+ }
171
193
if (candidate1 == candidate2) {
172
194
common.add (node);
173
195
}
174
196
}
175
197
}
176
198
177
199
if (common.length == 1 ) {
178
- return coreTypes.objectRawType (
179
- uniteNullabilities (type1.nullability, type2.nullability));
200
+ assert (common.single.classBuilder.cls == coreTypes.objectClass);
201
+ return coreTypes.objectRawType (uniteNullabilities (
202
+ type1NullabilityForResult, type2NullabilityForResult));
180
203
}
181
204
common.sort (ClassHierarchyNode .compareMaxInheritancePath);
182
205
183
206
for (int i = 0 ; i < common.length - 1 ; i++ ) {
184
207
ClassHierarchyNode node = common[i];
185
208
if (node.maxInheritancePath != common[i + 1 ].maxInheritancePath) {
186
- return getTypeAsInstanceOf (type1, node.classBuilder.cls,
187
- isNonNullableByDefault: isNonNullableByDefault)
188
- .withDeclaredNullability (
189
- uniteNullabilities (type1.nullability, type2.nullability));
209
+ if (type1 is InterfaceType ) {
210
+ return getTypeAsInstanceOf (type1, node.classBuilder.cls,
211
+ isNonNullableByDefault: isNonNullableByDefault)
212
+ .withDeclaredNullability (uniteNullabilities (
213
+ type1NullabilityForResult, type2NullabilityForResult));
214
+ } else {
215
+ type1 as ExtensionType ;
216
+ return getExtensionTypeAsInstanceOfClass (type1, node.classBuilder.cls,
217
+ isNonNullableByDefault: isNonNullableByDefault)!
218
+ .withDeclaredNullability (uniteNullabilities (
219
+ type1NullabilityForResult, type2NullabilityForResult));
220
+ }
190
221
} else {
191
222
do {
192
223
i++ ;
193
224
} while (node.maxInheritancePath == common[i + 1 ].maxInheritancePath);
194
225
}
195
226
}
196
- return coreTypes.objectRawType (
197
- uniteNullabilities (type1.nullability, type2.nullability));
227
+ return coreTypes.objectRawType (uniteNullabilities (
228
+ type1NullabilityForResult, type2NullabilityForResult));
229
+ }
230
+
231
+ @override
232
+ InterfaceType getLegacyLeastUpperBound (
233
+ InterfaceType type1, InterfaceType type2,
234
+ {required bool isNonNullableByDefault}) {
235
+ if (type1 == type2) return type1;
236
+
237
+ // LLUB(Null, List<dynamic>*) works differently for opt-in and opt-out
238
+ // libraries. In opt-out libraries the legacy behavior is preserved, so
239
+ // LLUB(Null, List<dynamic>*) = List<dynamic>*. In opt-out libraries the
240
+ // rules imply that LLUB(Null, List<dynamic>*) = List<dynamic>?.
241
+ if (! isNonNullableByDefault) {
242
+ if (type1 is NullType ) {
243
+ return type2;
244
+ }
245
+ if (type2 is NullType ) {
246
+ return type1;
247
+ }
248
+ }
249
+
250
+ return getLegacyLeastUpperBoundFromSupertypeLists (
251
+ type1, type2, < InterfaceType > [type1], < InterfaceType > [type2],
252
+ isNonNullableByDefault: isNonNullableByDefault);
253
+ }
254
+
255
+ @override
256
+ InterfaceType getLegacyLeastUpperBoundFromSupertypeLists (
257
+ /* InterfaceType | ExtensionType */ DartType type1,
258
+ /* InterfaceType | ExtensionType */ DartType type2,
259
+ List <InterfaceType > supertypes1,
260
+ List <InterfaceType > supertypes2,
261
+ {required bool isNonNullableByDefault}) {
262
+ List <ClassHierarchyNode > supertypeNodes1 = < ClassHierarchyNode > [
263
+ for (InterfaceType supertype in supertypes1)
264
+ ...getNodeFromClass (supertype.classNode).computeAllSuperNodes (this )
265
+ ];
266
+ List <ClassHierarchyNode > supertypeNodes2 = < ClassHierarchyNode > [
267
+ for (InterfaceType supertype in supertypes2)
268
+ ...getNodeFromClass (supertype.classNode).computeAllSuperNodes (this )
269
+ ];
270
+
271
+ return _getLegacyLeastUpperBoundInternal (
272
+ type1, type2, supertypeNodes1, supertypeNodes2,
273
+ isNonNullableByDefault: isNonNullableByDefault);
198
274
}
199
275
200
276
static ClassHierarchyBuilder build (
0 commit comments