Skip to content

Commit b9b1fdf

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[cfe] Infer type and finality of joint variables in or-patterns
Closes #52099 Change-Id: I019e0a9e38cee35883c197ebae855b0854a0b2ab Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/297640 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 34c18da commit b9b1fdf

24 files changed

+418
-64
lines changed

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,10 +1922,13 @@ class InferenceVisitorImpl extends InferenceVisitorBase
19221922
int? stackBase;
19231923
assert(checkStackBase(node, stackBase = stackHeight));
19241924

1925-
// TODO(scheglov) Pass actual variables, not just `{}`.
19261925
IfCaseStatementResult<DartType, InvalidExpression> analysisResult =
19271926
analyzeIfCaseStatement(node, node.expression, node.patternGuard.pattern,
1928-
node.patternGuard.guard, node.then, node.otherwise, {});
1927+
node.patternGuard.guard, node.then, node.otherwise, {
1928+
for (VariableDeclaration variable
1929+
in node.patternGuard.pattern.declaredVariables)
1930+
variable.name!: variable
1931+
});
19291932

19301933
node.matchedValueType = analysisResult.matchedExpressionType;
19311934

@@ -10058,23 +10061,28 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1005810061
for (VariableDeclaration variable in node.left.declaredVariables)
1005910062
variable.name!: variable
1006010063
};
10061-
Set<String> jointVariableNames = {
10064+
Map<String, VariableDeclaration> jointVariableNames = {
1006210065
for (VariableDeclaration variable in node.orPatternJointVariables)
10063-
variable.name!
10066+
variable.name!: variable
1006410067
};
1006510068
for (VariableDeclaration rightVariable in node.right.declaredVariables) {
1006610069
String rightVariableName = rightVariable.name!;
1006710070
VariableDeclaration? leftVariable =
1006810071
leftDeclaredVariablesByName[rightVariableName];
10069-
if (leftVariable != null &&
10070-
jointVariableNames.contains(rightVariableName) &&
10071-
(leftVariable.type != rightVariable.type ||
10072-
leftVariable.isFinal != rightVariable.isFinal)) {
10073-
helper.addProblem(
10074-
templateJointPatternVariablesMismatch
10075-
.withArguments(rightVariableName),
10076-
leftVariable.fileOffset,
10077-
rightVariableName.length);
10072+
VariableDeclaration? jointVariable =
10073+
jointVariableNames[rightVariableName];
10074+
if (leftVariable != null && jointVariable != null) {
10075+
if (leftVariable.type != rightVariable.type ||
10076+
leftVariable.isFinal != rightVariable.isFinal) {
10077+
helper.addProblem(
10078+
templateJointPatternVariablesMismatch
10079+
.withArguments(rightVariableName),
10080+
leftVariable.fileOffset,
10081+
rightVariableName.length);
10082+
} else {
10083+
jointVariable.isFinal = rightVariable.isFinal;
10084+
jointVariable.type = rightVariable.type;
10085+
}
1007810086
}
1007910087
}
1008010088

pkg/front_end/testcases/patterns/issue51739.dart.strong.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import "dart:core" as core;
1212
static method method() → dynamic {
1313
core::int? q = (2 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::int?;
1414
{
15-
hoisted dynamic x;
15+
hoisted invalid-type x;
1616
final synthesized core::int? #0#0 = q;
1717
if((let final dynamic #t1 = #0#0! in let final dynamic #t2 = x = #0#0! in true) || #0#0 is{ForNonNullableByDefault} core::String && (let final dynamic #t3 = x = #0#0 in true)) {
1818
core::print(x);

pkg/front_end/testcases/patterns/issue51739.dart.strong.transformed.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import "dart:core" as core;
1212
static method method() → dynamic {
1313
core::int? q = (2 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::int?;
1414
{
15-
hoisted dynamic x;
15+
hoisted invalid-type x;
1616
final synthesized core::int? #0#0 = q;
1717
if((let final core::int? #t1 = #0#0! in let final core::int? #t2 = x = #0#0! in true) || #0#0 is{ForNonNullableByDefault} core::String && (let final core::int? #t3 = x = #0#0 in true)) {
1818
core::print(x);

pkg/front_end/testcases/patterns/issue51739.dart.weak.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import "dart:core" as core;
1212
static method method() → dynamic {
1313
core::int? q = (2 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::int?;
1414
{
15-
hoisted dynamic x;
15+
hoisted invalid-type x;
1616
final synthesized core::int? #0#0 = q;
1717
if((let final dynamic #t1 = #0#0! in let final dynamic #t2 = x = #0#0! in true) || #0#0 is{ForNonNullableByDefault} core::String && (let final dynamic #t3 = x = #0#0 in true)) {
1818
core::print(x);

pkg/front_end/testcases/patterns/issue51739.dart.weak.modular.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import "dart:core" as core;
1212
static method method() → dynamic {
1313
core::int? q = (2 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::int?;
1414
{
15-
hoisted dynamic x;
15+
hoisted invalid-type x;
1616
final synthesized core::int? #0#0 = q;
1717
if((let final dynamic #t1 = #0#0! in let final dynamic #t2 = x = #0#0! in true) || #0#0 is{ForNonNullableByDefault} core::String && (let final dynamic #t3 = x = #0#0 in true)) {
1818
core::print(x);

pkg/front_end/testcases/patterns/issue51739.dart.weak.transformed.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import "dart:core" as core;
1212
static method method() → dynamic {
1313
core::int? q = (2 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::int?;
1414
{
15-
hoisted dynamic x;
15+
hoisted invalid-type x;
1616
final synthesized core::int? #0#0 = q;
1717
if((let final core::int? #t1 = #0#0! in let final core::int? #t2 = x = #0#0! in true) || #0#0 is{ForNonNullableByDefault} core::String && (let final core::int? #t3 = x = #0#0 in true)) {
1818
core::print(x);

pkg/front_end/testcases/patterns/issue51971_2.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
test1(dynamic x1) {
6-
if (x1 case int a1 && < /*a*/1 || int a1) { // Error.
6+
if (x1 case int a1 && < a1 || int a1) { // Error.
77
return a1;
8-
} else if (x1 case int a1 || int a1 && < /*a*/1) { // Error.
8+
} else if (x1 case int a1 || int a1 && < a1) { // Error.
99
return a1;
1010
} else {
1111
return null;

pkg/front_end/testcases/patterns/issue51971_2.dart.strong.expect

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ library /*isNonNullableByDefault*/;
22
//
33
// Problems in library:
44
//
5+
// pkg/front_end/testcases/patterns/issue51971_2.dart:6:27: Error: Read of a non-const variable is not a constant expression.
6+
// if (x1 case int a1 && < a1 || int a1) { // Error.
7+
// ^
8+
//
9+
// pkg/front_end/testcases/patterns/issue51971_2.dart:8:44: Error: Read of a non-const variable is not a constant expression.
10+
// } else if (x1 case int a1 || int a1 && < a1) { // Error.
11+
// ^
12+
//
513
// pkg/front_end/testcases/patterns/issue51971_2.dart:27:22: Error: Read of a non-const variable is not a constant expression.
614
// case int a3 && < a3: // Error.
715
// ^
@@ -28,15 +36,17 @@ import "dart:collection" as col;
2836

2937
static method test1(dynamic x1) → dynamic {
3038
{
31-
hoisted dynamic a1;
39+
hoisted core::int a1;
3240
final synthesized dynamic #0#0 = x1;
33-
if(#0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t1 = a1 = #0#0 in true) && #0#0.{core::num::<}(#C1){(core::num) → core::bool} || #0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = a1 = #0#0 in true)) {
41+
final const synthesized core::int #0#1 = invalid-expression "Read of a non-const variable is not a constant expression.";
42+
if(#0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t1 = a1 = #0#0{core::int} in true) && #0#0.{core::num::<}(invalid-expression "Read of a non-const variable is not a constant expression."){(core::num) → core::bool} || #0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = a1 = #0#0{core::int} in true)) {
3443
return a1;
3544
}
3645
else {
37-
hoisted dynamic a1;
46+
hoisted core::int a1;
3847
final synthesized dynamic #1#0 = x1;
39-
if(#1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = a1 = #1#0 in true) || #1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t4 = a1 = #1#0 in true) && #1#0.{core::num::<}(#C1){(core::num) → core::bool}) {
48+
final const synthesized core::int #1#1 = invalid-expression "Read of a non-const variable is not a constant expression.";
49+
if(#1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = a1 = #1#0{core::int} in true) || #1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t4 = a1 = #1#0{core::int} in true) && #1#0.{core::num::<}(invalid-expression "Read of a non-const variable is not a constant expression."){(core::num) → core::bool}) {
4050
return a1;
4151
}
4252
else {
@@ -147,7 +157,7 @@ static method test6(dynamic x6) → dynamic {
147157
final synthesized dynamic #0#0 = x6 as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<core::int>;
148158
late final synthesized dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
149159
late final synthesized dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
150-
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C2 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t14 = i6 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t15 = n6 = #0#7{core::int} in true))))
160+
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t14 = i6 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t15 = n6 = #0#7{core::int} in true))))
151161
throw new core::StateError::•("Pattern matching error");
152162
}
153163
final core::int #t16 = i6;
@@ -164,7 +174,7 @@ static method test7(dynamic x7) → dynamic {
164174
final synthesized dynamic #0#0 = x7 as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<core::int>;
165175
late final synthesized dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
166176
late final synthesized dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
167-
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C2 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t18 = i7 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t19 = n7 = #0#7{core::int} in true))))
177+
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t18 = i7 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t19 = n7 = #0#7{core::int} in true))))
168178
throw new core::StateError::•("Pattern matching error");
169179
}
170180
final core::int #t20 = i7;
@@ -178,6 +188,5 @@ static method test7(dynamic x7) → dynamic {
178188
}
179189

180190
constants {
181-
#C1 = 1
182-
#C2 = 2
191+
#C1 = 2
183192
}

pkg/front_end/testcases/patterns/issue51971_2.dart.weak.expect

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ library /*isNonNullableByDefault*/;
22
//
33
// Problems in library:
44
//
5+
// pkg/front_end/testcases/patterns/issue51971_2.dart:6:27: Error: Read of a non-const variable is not a constant expression.
6+
// if (x1 case int a1 && < a1 || int a1) { // Error.
7+
// ^
8+
//
9+
// pkg/front_end/testcases/patterns/issue51971_2.dart:8:44: Error: Read of a non-const variable is not a constant expression.
10+
// } else if (x1 case int a1 || int a1 && < a1) { // Error.
11+
// ^
12+
//
513
// pkg/front_end/testcases/patterns/issue51971_2.dart:27:22: Error: Read of a non-const variable is not a constant expression.
614
// case int a3 && < a3: // Error.
715
// ^
@@ -29,15 +37,17 @@ import "dart:collection" as col;
2937

3038
static method test1(dynamic x1) → dynamic {
3139
{
32-
hoisted dynamic a1;
40+
hoisted core::int a1;
3341
final synthesized dynamic #0#0 = x1;
34-
if(#0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t1 = a1 = #0#0 in true) && #0#0.{core::num::<}(#C1){(core::num) → core::bool} || #0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = a1 = #0#0 in true)) {
42+
final const synthesized core::int #0#1 = invalid-expression "Read of a non-const variable is not a constant expression.";
43+
if(#0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t1 = a1 = #0#0{core::int} in true) && #0#0.{core::num::<}(invalid-expression "Read of a non-const variable is not a constant expression."){(core::num) → core::bool} || #0#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t2 = a1 = #0#0{core::int} in true)) {
3544
return a1;
3645
}
3746
else {
38-
hoisted dynamic a1;
47+
hoisted core::int a1;
3948
final synthesized dynamic #1#0 = x1;
40-
if(#1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = a1 = #1#0 in true) || #1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t4 = a1 = #1#0 in true) && #1#0.{core::num::<}(#C1){(core::num) → core::bool}) {
49+
final const synthesized core::int #1#1 = invalid-expression "Read of a non-const variable is not a constant expression.";
50+
if(#1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t3 = a1 = #1#0{core::int} in true) || #1#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t4 = a1 = #1#0{core::int} in true) && #1#0.{core::num::<}(invalid-expression "Read of a non-const variable is not a constant expression."){(core::num) → core::bool}) {
4151
return a1;
4252
}
4353
else {
@@ -149,7 +159,7 @@ static method test6(dynamic x6) → dynamic {
149159
final synthesized dynamic #0#0 = x6 as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<core::int>;
150160
late final synthesized dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
151161
late final synthesized dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
152-
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C2 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t14 = i6 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t15 = n6 = #0#7{core::int} in true))))
162+
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t14 = i6 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t15 = n6 = #0#7{core::int} in true))))
153163
throw new core::StateError::•("Pattern matching error");
154164
}
155165
final core::int #t16 = i6;
@@ -166,7 +176,7 @@ static method test7(dynamic x7) → dynamic {
166176
final synthesized dynamic #0#0 = x7 as{TypeError,ForDynamic,ForNonNullableByDefault} core::List<core::int>;
167177
late final synthesized dynamic #0#6 = #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic};
168178
late final synthesized dynamic #0#7 = #0#0{core::List<dynamic>}.{core::List::[]}(1){(core::int) → dynamic};
169-
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C2 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t18 = i7 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t19 = n7 = #0#7{core::int} in true))))
179+
if(!(#0#0 is{ForNonNullableByDefault} core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (#0#6 is{ForNonNullableByDefault} core::int && (let final dynamic #t18 = i7 = #0#6{core::int} in true)) && (#0#7 is{ForNonNullableByDefault} core::int && (let final dynamic #t19 = n7 = #0#7{core::int} in true))))
170180
throw new core::StateError::•("Pattern matching error");
171181
}
172182
final core::int #t20 = i7;
@@ -180,6 +190,5 @@ static method test7(dynamic x7) → dynamic {
180190
}
181191

182192
constants {
183-
#C1 = 1
184-
#C2 = 2
193+
#C1 = 2
185194
}

0 commit comments

Comments
 (0)