Skip to content

Commit 96a346f

Browse files
stereotype441Commit Queue
authored andcommitted
Front end: move anonymous mixin sealed/final inference to checkSupertypes phase.
Previously this step happened during `buildOutlineNodes`, but since `buildOutlineNodes` happens in source order, this means that anonymous mixins would only get their sealed and final attributes properly inferred if they appeared *after* their immediate supertypes in source order. By moving this step to `checkSupertypes`, we ensure that the computation is correct regardless of source order, because `checkSupertypes` happens in class hierarchy order. Fixes #52048. Bug: #52048 Change-Id: Ib9f1f3dafded88681a26f09e4d21dfd44e70dfd3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/297901 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent dc77baa commit 96a346f

11 files changed

+302
-22
lines changed

pkg/front_end/lib/src/fasta/source/source_class_builder.dart

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -286,28 +286,6 @@ class SourceClassBuilder extends ClassBuilderImpl
286286
cls.isInterface = isInterface;
287287
cls.isFinal = isFinal;
288288

289-
// Anonymous mixins have to propagate certain class modifiers.
290-
if (cls.isAnonymousMixin) {
291-
Class? superclass = cls.superclass;
292-
Class? mixedInClass = cls.mixedInClass;
293-
// If either [superclass] or [mixedInClass] is sealed, the current
294-
// anonymous mixin is sealed.
295-
if (superclass != null && superclass.isSealed ||
296-
mixedInClass != null && mixedInClass.isSealed) {
297-
cls.isSealed = true;
298-
} else {
299-
// Otherwise, if either [superclass] or [mixedInClass] is base or final,
300-
// then the current anonymous mixin is final.
301-
bool superclassIsBaseOrFinal =
302-
superclass != null && (superclass.isBase || superclass.isFinal);
303-
bool mixedInClassIsBaseOrFinal = mixedInClass != null &&
304-
(mixedInClass.isBase || mixedInClass.isFinal);
305-
if (superclassIsBaseOrFinal || mixedInClassIsBaseOrFinal) {
306-
cls.isFinal = true;
307-
}
308-
}
309-
}
310-
311289
if (interfaceBuilders != null) {
312290
for (int i = 0; i < interfaceBuilders!.length; ++i) {
313291
interfaceBuilders![i] = _checkSupertype(interfaceBuilders![i]);
@@ -641,6 +619,28 @@ class SourceClassBuilder extends ClassBuilderImpl
641619
// Moreover, it checks that `FutureOr` and `void` are not among the
642620
// supertypes and that `Enum` is not implemented by non-abstract classes.
643621

622+
// Anonymous mixins have to propagate certain class modifiers.
623+
if (cls.isAnonymousMixin) {
624+
Class? superclass = cls.superclass;
625+
Class? mixedInClass = cls.mixedInClass;
626+
// If either [superclass] or [mixedInClass] is sealed, the current
627+
// anonymous mixin is sealed.
628+
if (superclass != null && superclass.isSealed ||
629+
mixedInClass != null && mixedInClass.isSealed) {
630+
cls.isSealed = true;
631+
} else {
632+
// Otherwise, if either [superclass] or [mixedInClass] is base or final,
633+
// then the current anonymous mixin is final.
634+
bool superclassIsBaseOrFinal =
635+
superclass != null && (superclass.isBase || superclass.isFinal);
636+
bool mixedInClassIsBaseOrFinal = mixedInClass != null &&
637+
(mixedInClass.isBase || mixedInClass.isFinal);
638+
if (superclassIsBaseOrFinal || mixedInClassIsBaseOrFinal) {
639+
cls.isFinal = true;
640+
}
641+
}
642+
}
643+
644644
ClassHierarchyNode classHierarchyNode =
645645
hierarchyBuilder.getNodeFromClass(cls);
646646
if (libraryBuilder.libraryFeatures.enhancedEnums.isEnabled && !isEnum) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2023, 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+
main() {
6+
f(B());
7+
}
8+
9+
f(A a) {
10+
switch (a) {
11+
case B():
12+
print("It's a B");
13+
}
14+
}
15+
16+
class B extends A with M {
17+
B();
18+
}
19+
20+
sealed class A {}
21+
22+
mixin M {}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
abstract sealed class _B&A&M = self::A with self::M /*isAnonymousMixin*/ {
6+
synthetic constructor •() → self::_B&A&M
7+
: super self::A::•()
8+
;
9+
}
10+
class B extends self::_B&A&M {
11+
constructor •() → self::B
12+
: super self::_B&A&M::•()
13+
;
14+
}
15+
abstract sealed class A extends core::Object {
16+
synthetic constructor •() → self::A
17+
: super core::Object::•()
18+
;
19+
}
20+
abstract class M extends core::Object /*isMixinDeclaration*/ {
21+
}
22+
static method main() → dynamic {
23+
self::f(new self::B::•());
24+
}
25+
static method f(self::A a) → dynamic {
26+
#L1:
27+
{
28+
final synthesized self::A #0#0 = a;
29+
{
30+
if(#0#0 is{ForNonNullableByDefault} self::B) {
31+
{
32+
core::print("It's a B");
33+
}
34+
}
35+
}
36+
}
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
abstract sealed class _B&A&M extends self::A implements self::M /*isAnonymousMixin,isEliminatedMixin*/ {
6+
synthetic constructor •() → self::_B&A&M
7+
: super self::A::•()
8+
;
9+
}
10+
class B extends self::_B&A&M {
11+
constructor •() → self::B
12+
: super self::_B&A&M::•()
13+
;
14+
}
15+
abstract sealed class A extends core::Object {
16+
synthetic constructor •() → self::A
17+
: super core::Object::•()
18+
;
19+
}
20+
abstract class M extends core::Object /*isMixinDeclaration*/ {
21+
}
22+
static method main() → dynamic {
23+
self::f(new self::B::•());
24+
}
25+
static method f(self::A a) → dynamic {
26+
#L1:
27+
{
28+
final synthesized self::A #0#0 = a;
29+
{
30+
if(#0#0 is{ForNonNullableByDefault} self::B) {
31+
{
32+
core::print("It's a B");
33+
}
34+
}
35+
}
36+
}
37+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
main() {}
2+
f(A a) {}
3+
4+
class B extends A with M {
5+
B();
6+
}
7+
8+
sealed class A {}
9+
10+
mixin M {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class B extends A with M {
2+
B();
3+
}
4+
5+
f(A a) {}
6+
main() {}
7+
mixin M {}
8+
9+
sealed class A {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
abstract sealed class _B&A&M = self::A with self::M /*isAnonymousMixin*/ {
7+
synthetic constructor •() → self::_B&A&M
8+
: super self::A::•()
9+
;
10+
}
11+
class B extends self::_B&A&M {
12+
constructor •() → self::B
13+
: super self::_B&A&M::•()
14+
;
15+
}
16+
abstract sealed class A extends core::Object {
17+
synthetic constructor •() → self::A
18+
: super core::Object::•()
19+
;
20+
}
21+
abstract class M extends core::Object /*isMixinDeclaration*/ {
22+
}
23+
static method main() → dynamic {
24+
self::f(new self::B::•());
25+
}
26+
static method f(self::A a) → dynamic {
27+
#L1:
28+
{
29+
final synthesized self::A #0#0 = a;
30+
{
31+
if(#0#0 is{ForNonNullableByDefault} self::B) {
32+
{
33+
core::print("It's a B");
34+
}
35+
}
36+
break #L1;
37+
}
38+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
abstract sealed class _B&A&M = self::A with self::M /*isAnonymousMixin*/ {
7+
synthetic constructor •() → self::_B&A&M
8+
: super self::A::•()
9+
;
10+
}
11+
class B extends self::_B&A&M {
12+
constructor •() → self::B
13+
: super self::_B&A&M::•()
14+
;
15+
}
16+
abstract sealed class A extends core::Object {
17+
synthetic constructor •() → self::A
18+
: super core::Object::•()
19+
;
20+
}
21+
abstract class M extends core::Object /*isMixinDeclaration*/ {
22+
}
23+
static method main() → dynamic {
24+
self::f(new self::B::•());
25+
}
26+
static method f(self::A a) → dynamic {
27+
#L1:
28+
{
29+
final synthesized self::A #0#0 = a;
30+
{
31+
if(#0#0 is{ForNonNullableByDefault} self::B) {
32+
{
33+
core::print("It's a B");
34+
}
35+
}
36+
break #L1;
37+
}
38+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
39+
}
40+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
abstract sealed class _B&A&M = self::A with self::M /*isAnonymousMixin*/ {
6+
synthetic constructor •() → self::_B&A&M
7+
: super self::A::•()
8+
;
9+
}
10+
class B extends self::_B&A&M {
11+
constructor •() → self::B
12+
;
13+
}
14+
abstract sealed class A extends core::Object {
15+
synthetic constructor •() → self::A
16+
;
17+
}
18+
abstract class M extends core::Object /*isMixinDeclaration*/ {
19+
}
20+
static method main() → dynamic
21+
;
22+
static method f(self::A a) → dynamic
23+
;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
abstract sealed class _B&A&M extends self::A implements self::M /*isAnonymousMixin,isEliminatedMixin*/ {
7+
synthetic constructor •() → self::_B&A&M
8+
: super self::A::•()
9+
;
10+
}
11+
class B extends self::_B&A&M {
12+
constructor •() → self::B
13+
: super self::_B&A&M::•()
14+
;
15+
}
16+
abstract sealed class A extends core::Object {
17+
synthetic constructor •() → self::A
18+
: super core::Object::•()
19+
;
20+
}
21+
abstract class M extends core::Object /*isMixinDeclaration*/ {
22+
}
23+
static method main() → dynamic {
24+
self::f(new self::B::•());
25+
}
26+
static method f(self::A a) → dynamic {
27+
#L1:
28+
{
29+
final synthesized self::A #0#0 = a;
30+
{
31+
if(#0#0 is{ForNonNullableByDefault} self::B) {
32+
{
33+
core::print("It's a B");
34+
}
35+
}
36+
break #L1;
37+
}
38+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
39+
}
40+
}

0 commit comments

Comments
 (0)