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

Commit 0f141be

Browse files
Dmitry Stefantsovcommit-bot@chromium.org
authored andcommitted
[cfe] Implement new rules around types of case expressions
Bug: http://dartbug.com/40425 Change-Id: I1f6e93bde663ab6f1a6a63e91aef4133af6e5253 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136186 Commit-Queue: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
1 parent 17d8b6b commit 0f141be

11 files changed

+460
-17
lines changed

pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3577,6 +3577,43 @@ Message _withArgumentsSwitchExpressionNotAssignable(
35773577
arguments: {'type': _type, 'type2': _type2});
35783578
}
35793579

3580+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
3581+
const Template<
3582+
Message Function(
3583+
DartType _type, DartType _type2, bool isNonNullableByDefault)>
3584+
templateSwitchExpressionNotSubtype = const Template<
3585+
Message Function(
3586+
DartType _type, DartType _type2, bool isNonNullableByDefault)>(
3587+
messageTemplate:
3588+
r"""Type '#type' of the case expression is not a subtype of type '#type2' of this switch expression.""",
3589+
withArguments: _withArgumentsSwitchExpressionNotSubtype);
3590+
3591+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
3592+
const Code<
3593+
Message Function(
3594+
DartType _type, DartType _type2, bool isNonNullableByDefault)>
3595+
codeSwitchExpressionNotSubtype = const Code<
3596+
Message Function(
3597+
DartType _type, DartType _type2, bool isNonNullableByDefault)>(
3598+
"SwitchExpressionNotSubtype",
3599+
templateSwitchExpressionNotSubtype,
3600+
);
3601+
3602+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
3603+
Message _withArgumentsSwitchExpressionNotSubtype(
3604+
DartType _type, DartType _type2, bool isNonNullableByDefault) {
3605+
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
3606+
List<Object> typeParts = labeler.labelType(_type);
3607+
List<Object> type2Parts = labeler.labelType(_type2);
3608+
String type = typeParts.join();
3609+
String type2 = type2Parts.join();
3610+
return new Message(codeSwitchExpressionNotSubtype,
3611+
message:
3612+
"""Type '${type}' of the case expression is not a subtype of type '${type2}' of this switch expression.""" +
3613+
labeler.originMessages,
3614+
arguments: {'type': _type, 'type2': _type2});
3615+
}
3616+
35803617
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
35813618
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
35823619
templateThrowingNotAssignableToObjectError = const Template<

pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5154,22 +5154,43 @@ class InferenceVisitor
51545154
switchCase.expressions[index] = caseExpression..parent = switchCase;
51555155
DartType caseExpressionType = caseExpressionResult.inferredType;
51565156

5157-
// Check whether the expression type is assignable to the case
5158-
// expression type.
5159-
if (!inferrer.isAssignable(expressionType, caseExpressionType)) {
5160-
inferrer.helper.addProblem(
5161-
templateSwitchExpressionNotAssignable.withArguments(
5162-
expressionType,
5163-
caseExpressionType,
5164-
inferrer.isNonNullableByDefault),
5165-
caseExpression.fileOffset,
5166-
noLength,
5167-
context: [
5168-
messageSwitchExpressionNotAssignableCause.withLocation(
5169-
inferrer.uriForInstrumentation,
5170-
node.expression.fileOffset,
5171-
noLength)
5172-
]);
5157+
if (inferrer.library.isNonNullableByDefault) {
5158+
if (inferrer.performNnbdChecks) {
5159+
if (!inferrer.typeSchemaEnvironment.isSubtypeOf(caseExpressionType,
5160+
expressionType, SubtypeCheckMode.withNullabilities)) {
5161+
inferrer.helper.addProblem(
5162+
templateSwitchExpressionNotSubtype.withArguments(
5163+
caseExpressionType,
5164+
expressionType,
5165+
inferrer.isNonNullableByDefault),
5166+
caseExpression.fileOffset,
5167+
noLength,
5168+
context: [
5169+
messageSwitchExpressionNotAssignableCause.withLocation(
5170+
inferrer.uriForInstrumentation,
5171+
node.expression.fileOffset,
5172+
noLength)
5173+
]);
5174+
}
5175+
}
5176+
} else {
5177+
// Check whether the expression type is assignable to the case
5178+
// expression type.
5179+
if (!inferrer.isAssignable(expressionType, caseExpressionType)) {
5180+
inferrer.helper.addProblem(
5181+
templateSwitchExpressionNotAssignable.withArguments(
5182+
expressionType,
5183+
caseExpressionType,
5184+
inferrer.isNonNullableByDefault),
5185+
caseExpression.fileOffset,
5186+
noLength,
5187+
context: [
5188+
messageSwitchExpressionNotAssignableCause.withLocation(
5189+
inferrer.uriForInstrumentation,
5190+
node.expression.fileOffset,
5191+
noLength)
5192+
]);
5193+
}
51735194
}
51745195
}
51755196
StatementInferenceResult bodyResult =

pkg/front_end/messages.status

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,10 @@ SupertypeIsFunction/example: Fail
622622
SupertypeIsIllegal/example: Fail
623623
SupertypeIsTypeVariable/example: Fail
624624
SwitchCaseFallThrough/example: Fail
625+
SwitchExpressionNotSubtype/analyzerCode: Fail
626+
SwitchExpressionNotSubtype/example: Fail
627+
SwitchExpressionUserDefinedEquals/analyzerCode: Fail
628+
SwitchExpressionUserDefinedEquals/example: Fail
625629
SyntheticToken/example: Fail # Can't be tested, used to recover from other errors.
626630
ThisAccessInFieldInitializer/example: Fail
627631
ThisAsIdentifier/example: Fail

pkg/front_end/messages.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,6 +2615,9 @@ SwitchExpressionNotAssignableCause:
26152615
template: "The switch expression is here."
26162616
severity: CONTEXT
26172617

2618+
SwitchExpressionNotSubtype:
2619+
template: "Type '#type' of the case expression is not a subtype of type '#type2' of this switch expression."
2620+
26182621
SwitchHasCaseAfterDefault:
26192622
index: 16
26202623
template: "The default case should be the last case in a switch statement."
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) 2020, 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+
// The test checks the constraints for types of the case expressions. The rules
6+
// can be found at the following link:
7+
// https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md#errors-and-warnings
8+
9+
class A {
10+
final int foo;
11+
const A(this.foo);
12+
}
13+
14+
class B extends A {
15+
const B(int foo) : super(foo);
16+
}
17+
18+
class C extends B {
19+
const C(int foo) : super(foo);
20+
}
21+
22+
class D extends B {
23+
const D(int foo) : super(foo);
24+
25+
bool operator ==(dynamic other) => identical(this, other);
26+
}
27+
28+
bar(B b) {
29+
switch (b) {
30+
case const B(42):
31+
break;
32+
case const C(42):
33+
break;
34+
case const A(42): // Error: not a subtype of B.
35+
break;
36+
case const D(42): // Error: D has custom operator ==.
37+
break;
38+
default:
39+
}
40+
}
41+
42+
main() {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class A extends core::Object /*hasConstConstructor*/ {
6+
final field core::int foo;
7+
const constructor •(core::int foo) → self::A
8+
: self::A::foo = foo, super core::Object::•()
9+
;
10+
}
11+
class B extends self::A /*hasConstConstructor*/ {
12+
const constructor •(core::int foo) → self::B
13+
: super self::A::•(foo)
14+
;
15+
}
16+
class C extends self::B /*hasConstConstructor*/ {
17+
const constructor •(core::int foo) → self::C
18+
: super self::B::•(foo)
19+
;
20+
}
21+
class D extends self::B /*hasConstConstructor*/ {
22+
const constructor •(core::int foo) → self::D
23+
: super self::B::•(foo)
24+
;
25+
operator ==(dynamic other) → core::bool
26+
;
27+
}
28+
static method bar(self::B b) → dynamic
29+
;
30+
static method main() → dynamic
31+
;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/nnbd/switch_redesign_types.dart:34:16: Error: Type 'A' of the case expression is not a subtype of type 'B' of this switch expression.
6+
// - 'A' is from 'pkg/front_end/testcases/nnbd/switch_redesign_types.dart'.
7+
// - 'B' is from 'pkg/front_end/testcases/nnbd/switch_redesign_types.dart'.
8+
// case const A(42): // Error: not a subtype of B.
9+
// ^
10+
// pkg/front_end/testcases/nnbd/switch_redesign_types.dart:29:11: Context: The switch expression is here.
11+
// switch (b) {
12+
// ^
13+
//
14+
import self as self;
15+
import "dart:core" as core;
16+
17+
class A extends core::Object /*hasConstConstructor*/ {
18+
final field core::int foo;
19+
const constructor •(core::int foo) → self::A
20+
: self::A::foo = foo, super core::Object::•()
21+
;
22+
}
23+
class B extends self::A /*hasConstConstructor*/ {
24+
const constructor •(core::int foo) → self::B
25+
: super self::A::•(foo)
26+
;
27+
}
28+
class C extends self::B /*hasConstConstructor*/ {
29+
const constructor •(core::int foo) → self::C
30+
: super self::B::•(foo)
31+
;
32+
}
33+
class D extends self::B /*hasConstConstructor*/ {
34+
const constructor •(core::int foo) → self::D
35+
: super self::B::•(foo)
36+
;
37+
operator ==(dynamic other) → core::bool
38+
return core::identical(this, other);
39+
}
40+
static method bar(self::B b) → dynamic {
41+
#L1:
42+
switch(b) {
43+
#L2:
44+
case #C2:
45+
{
46+
break #L1;
47+
}
48+
#L3:
49+
case #C3:
50+
{
51+
break #L1;
52+
}
53+
#L4:
54+
case #C4:
55+
{
56+
break #L1;
57+
}
58+
#L5:
59+
case #C5:
60+
{
61+
break #L1;
62+
}
63+
#L6:
64+
default:
65+
{}
66+
}
67+
}
68+
static method main() → dynamic {}
69+
70+
constants {
71+
#C1 = 42
72+
#C2 = self::B {foo:#C1}
73+
#C3 = self::C {foo:#C1}
74+
#C4 = self::A {foo:#C1}
75+
#C5 = self::D {foo:#C1}
76+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/nnbd/switch_redesign_types.dart:34:16: Error: Type 'A' of the case expression is not a subtype of type 'B' of this switch expression.
6+
// - 'A' is from 'pkg/front_end/testcases/nnbd/switch_redesign_types.dart'.
7+
// - 'B' is from 'pkg/front_end/testcases/nnbd/switch_redesign_types.dart'.
8+
// case const A(42): // Error: not a subtype of B.
9+
// ^
10+
// pkg/front_end/testcases/nnbd/switch_redesign_types.dart:29:11: Context: The switch expression is here.
11+
// switch (b) {
12+
// ^
13+
//
14+
import self as self;
15+
import "dart:core" as core;
16+
17+
class A extends core::Object /*hasConstConstructor*/ {
18+
final field core::int foo;
19+
const constructor •(core::int foo) → self::A
20+
: self::A::foo = foo, super core::Object::•()
21+
;
22+
}
23+
class B extends self::A /*hasConstConstructor*/ {
24+
const constructor •(core::int foo) → self::B
25+
: super self::A::•(foo)
26+
;
27+
}
28+
class C extends self::B /*hasConstConstructor*/ {
29+
const constructor •(core::int foo) → self::C
30+
: super self::B::•(foo)
31+
;
32+
}
33+
class D extends self::B /*hasConstConstructor*/ {
34+
const constructor •(core::int foo) → self::D
35+
: super self::B::•(foo)
36+
;
37+
operator ==(dynamic other) → core::bool
38+
return core::identical(this, other);
39+
}
40+
static method bar(self::B b) → dynamic {
41+
#L1:
42+
switch(b) {
43+
#L2:
44+
case #C2:
45+
{
46+
break #L1;
47+
}
48+
#L3:
49+
case #C3:
50+
{
51+
break #L1;
52+
}
53+
#L4:
54+
case #C4:
55+
{
56+
break #L1;
57+
}
58+
#L5:
59+
case #C5:
60+
{
61+
break #L1;
62+
}
63+
#L6:
64+
default:
65+
{}
66+
}
67+
}
68+
static method main() → dynamic {}
69+
70+
constants {
71+
#C1 = 42
72+
#C2 = self::B {foo:#C1}
73+
#C3 = self::C {foo:#C1}
74+
#C4 = self::A {foo:#C1}
75+
#C5 = self::D {foo:#C1}
76+
}

0 commit comments

Comments
 (0)