Skip to content

Commit e7ef580

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[model] Adjust distance from top calculations in UP in Analyzer
Closes #61218 Change-Id: If50ca1619165f8060fcf18594b6c6f7ab7a4635c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/448382 Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Erik Ernst <[email protected]> Reviewed-by: Paul Berry <[email protected]>
1 parent 42c214b commit e7ef580

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,34 @@ class InterfaceLeastUpperBoundHelper {
268268
visitedElements,
269269
);
270270

271+
// Since mixin applications involve only one mixin, multiple mixins induce
272+
// multiple intermediate application classes. Consider the following
273+
// example:
274+
//
275+
// class A extends B with M1, M2, M3 {}
276+
//
277+
// Applying M1, M2, and M3 to B results in the following chain of
278+
// declarations.
279+
//
280+
// abstract class _A&B&M1 extends B implements M1 {}
281+
// abstract class _A&B&M1&M2 extends _A&B&M1 implements M2 {}
282+
// abstract class _A&B&M1&M2&M3 extends _AA&B&M1&M2 implements M3 {}
283+
// class A extends _A&B&M1&M2&M3 {}
284+
//
285+
// Each of the intermediate applications increase the distance to the top
286+
// type by 1.
287+
//
288+
// Note that named mixin applications are different in that the last
289+
// application is the named application itself, so its distance from the
290+
// top type is shorter by 1. Consider the following example.
291+
//
292+
// class A = B with M1, M2, M3;
293+
//
294+
// It will result in the following chain of declarations.
295+
//
296+
// abstract class _A&B&M1 extends B implements M1 {}
297+
// abstract class _A&B&M1&M2 extends _A&B&M1 implements M2 {}
298+
// class A extends _A&B&M1&M2 implements M3 {}
271299
var mixins = element.mixins;
272300
for (var i = 0; i < mixins.length; i++) {
273301
// class _X&S&M extends S implements M {}
@@ -280,6 +308,12 @@ class InterfaceLeastUpperBoundHelper {
280308
// For this synthetic class representing the mixin application.
281309
superLength++;
282310
}
311+
if (mixins.isNotEmpty &&
312+
element is ClassElementImpl &&
313+
element.isMixinApplication) {
314+
// In case of named mixin application, reduce the distance by 1.
315+
superLength--;
316+
}
283317

284318
longestPath = max(longestPath, 1 + superLength);
285319
} finally {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import '../static_type_helper.dart';
6+
7+
class A {} // Distance from the top type: 2 (Object? -> Object -> A).
8+
9+
class B1 {} // Distance from the top type: 2.
10+
11+
class B2 extends B1 {} // Distance from the top type: 3.
12+
13+
mixin M1 {} // Distance from the top type: 2.
14+
15+
mixin M2 implements B2 {} // Distance from the top type: 4.
16+
17+
mixin M3 {} // Distance from the top type: 2.
18+
19+
// The following class hierarchy is synthesized:
20+
//
21+
// - Object.
22+
// - Distance from the top type: 1.
23+
// - A extends Object.
24+
// - Distance from the top type: 2.
25+
// - _C&A&M1 extends A implements M1 (anonymous mixin application).
26+
// - Distance from the top type: 3.
27+
// - _C&A&M1&M2 extends _C&A&M1 implements M2 (anonymous).
28+
// - Distance from the top type: 5.
29+
// - _Note that M2 is of distance 4 from the top type._
30+
// - C extends _C&A&M1&M2 implements M3 (named mixin application).
31+
// - Distance from the top type: 6.
32+
class C1 = A with M1, M2, M3;
33+
34+
class C2 extends C1 {} // Distance from the top type: 7.
35+
36+
class D1 {} // Distance from the top type: 2.
37+
38+
class D2 extends D1 {} // Distance from the top type: 3.
39+
40+
class D3 extends D2 {} // Distance from the top type: 4.
41+
42+
class D4 extends D3 {} // Distance from the top type: 5.
43+
44+
class D5 extends D4 {} // Distance from the top type: 6.
45+
46+
class D6 extends D5 {} // Distance from the top type: 7.
47+
48+
class E extends C2 implements D6 {}
49+
50+
class F extends C2 implements D6 {}
51+
52+
test(bool b, E e, F f) {
53+
// Both E and F have two immediate supertypes, C2 and D6, of equal distance 7
54+
// from the top type. The UP algorithm for interface types rejects both of
55+
// them and tries the types closer to the top. The next two types in the
56+
// supertype chain are C1 and D5, which are of equals distance 6 from the top
57+
// type. Going higher up in the supertype chains, the anonymous mixin
58+
// application _C&A&M1&M2 is compared against D4, but anonymous mixin
59+
// applications are ignored as potential candidates for the outcome of UP, so
60+
// D4 will be chosen.
61+
var ef = b ? e : f;
62+
ef.expectStaticType<Exactly<D4>>();
63+
}
64+
65+
main() {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import '../static_type_helper.dart';
6+
7+
class A {} // Distance from the top type: 2 (Object? -> Object -> A).
8+
9+
mixin M {} // Distance from the top type: 2.
10+
class AM = A with M; // Distance from the top type: 3.
11+
12+
class B extends AM {} // Distance from the top type: 4.
13+
14+
class C {} // Distance from the top type: 2.
15+
16+
class D extends C {} // Distance from the top type: 3.
17+
18+
class E extends D {} // Distance from the top type: 4.
19+
20+
class F1 extends B implements E {}
21+
22+
class F2 extends B implements E {}
23+
24+
// AM2 is desugared as follows:
25+
//
26+
// - _AM2&A&M extends A implements M (anonymous mixin application).
27+
// - AM2 extends _AM2&A&M.
28+
class AM2 extends A with M {} // Distance from the top type: 4.
29+
30+
class B2 extends AM2 {} // Distance from the top type: 5.
31+
32+
class F12 extends B2 implements E {}
33+
34+
class F22 extends B2 implements E {}
35+
36+
void f(bool b, F1 f1, F2 f2, F12 f12, F22 f22) {
37+
// F1 and F2 have same two supertypes, B and E. Both of the supertypes are of
38+
// distance 4 from the top type. The UP algorithm ignores all of the types of
39+
// the same distance in case there are more than one of those. The next types
40+
// in the supertype chains are AM and D, which are of equal distance 3 from
41+
// the top type and are dismissed by the UP algorithms as potential candidates
42+
// for the upper bound. The next types the algorithm tries are A, M, and
43+
// C. They all have the same distance 2 from the top type. Finally, their
44+
// supertype, which is Object, is yielded as the result.
45+
var x = b ? f1 : f2;
46+
x.expectStaticType<Exactly<Object>>();
47+
48+
// F12 and F22 have same two supertypes, B2 and E. However, B2 is of a bigger
49+
// distance from the top type than E, so it's chosen as the upper bound by the
50+
// algorithm.
51+
var x2 = b ? f12 : f22;
52+
x2.expectStaticType<Exactly<B2>>();
53+
}
54+
55+
void main() {}

0 commit comments

Comments
 (0)