Skip to content

Commit f39f8f2

Browse files
johnniwintherCommit Queue
authored and
Commit Queue
committed
[cfe] Add implicit cast to dynamic guard
Closes #51724 Change-Id: I7db595557693addc31cbda4b70d00e1ba54671b7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/289906 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 5493d42 commit f39f8f2

32 files changed

+700
-44
lines changed

pkg/_fe_analyzer_shared/lib/src/type_inference/type_analysis_result.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,20 @@ class SwitchExpressionResult<Type extends Object, Error>
171171
/// This is `null` if no such errors where found.
172172
final Map<int, Error>? nonBooleanGuardErrors;
173173

174+
/// The types of the guard expressions.
175+
///
176+
/// The key is the case index of the guard.
177+
///
178+
/// This is `null` if no such guards where present.
179+
final Map<int, Type>? guardTypes;
180+
174181
/// Error for when the switch statement was non exhaustive.
175182
final Error? nonExhaustiveSwitchError;
176183

177184
SwitchExpressionResult(
178185
{required super.type,
179186
required this.nonBooleanGuardErrors,
187+
required this.guardTypes,
180188
required this.nonExhaustiveSwitchError});
181189
}
182190

@@ -214,6 +222,13 @@ class SwitchStatementTypeAnalysisResult<Type extends Object, Error> {
214222
/// This is `null` if no such errors where found.
215223
final Map<int, Map<int, Error>>? nonBooleanGuardErrors;
216224

225+
/// The types of the guard expressions.
226+
///
227+
/// The keys of the maps are case and head indices of the guard.
228+
///
229+
/// This is `null` if no such guards where present.
230+
final Map<int, Map<int, Type>>? guardTypes;
231+
217232
/// Error for when the switch statement was non exhaustive.
218233
final Error? nonExhaustiveSwitchError;
219234

@@ -225,6 +240,7 @@ class SwitchStatementTypeAnalysisResult<Type extends Object, Error> {
225240
required this.scrutineeType,
226241
required this.switchCaseCompletesNormallyErrors,
227242
required this.nonBooleanGuardErrors,
243+
required this.guardTypes,
228244
required this.nonExhaustiveSwitchError,
229245
});
230246
}
@@ -450,9 +466,13 @@ class IfCaseStatementResult<Type extends Object, Error> {
450466
/// Error for when the guard has a non-bool type.
451467
final Error? nonBooleanGuardError;
452468

469+
/// The type of the guard expression, if present.
470+
final Type? guardType;
471+
453472
IfCaseStatementResult(
454473
{required this.matchedExpressionType,
455-
required this.nonBooleanGuardError});
474+
required this.nonBooleanGuardError,
475+
required this.guardType});
456476
}
457477

458478
/// Result for analyzing a pattern-for-in statement or element in

pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -623,9 +623,10 @@ mixin TypeAnalyzer<
623623
variables, componentVariables, patternVariablePromotionKeys,
624624
location: JoinedPatternVariableLocation.singlePattern);
625625
Error? nonBooleanGuardError;
626+
Type? guardType;
626627
if (guard != null) {
627-
nonBooleanGuardError =
628-
_checkGuardType(guard, analyzeExpression(guard, boolType));
628+
guardType = analyzeExpression(guard, boolType);
629+
nonBooleanGuardError = _checkGuardType(guard, guardType);
629630
} else {
630631
handleNoGuard(node, 0);
631632
}
@@ -634,7 +635,8 @@ mixin TypeAnalyzer<
634635
_analyzeIfElementCommon(node, ifTrue, ifFalse, context);
635636
return new IfCaseStatementResult(
636637
matchedExpressionType: initializerType,
637-
nonBooleanGuardError: nonBooleanGuardError);
638+
nonBooleanGuardError: nonBooleanGuardError,
639+
guardType: guardType);
638640
}
639641

640642
/// Analyzes a statement of the form `if (expression case pattern) ifTrue` or
@@ -688,9 +690,10 @@ mixin TypeAnalyzer<
688690
handle_ifCaseStatement_afterPattern(node: node);
689691
// Stack: (Expression, Pattern)
690692
Error? nonBooleanGuardError;
693+
Type? guardType;
691694
if (guard != null) {
692-
nonBooleanGuardError =
693-
_checkGuardType(guard, analyzeExpression(guard, boolType));
695+
guardType = analyzeExpression(guard, boolType);
696+
nonBooleanGuardError = _checkGuardType(guard, guardType);
694697
} else {
695698
handleNoGuard(node, 0);
696699
}
@@ -699,7 +702,8 @@ mixin TypeAnalyzer<
699702
_analyzeIfCommon(node, ifTrue, ifFalse);
700703
return new IfCaseStatementResult(
701704
matchedExpressionType: initializerType,
702-
nonBooleanGuardError: nonBooleanGuardError);
705+
nonBooleanGuardError: nonBooleanGuardError,
706+
guardType: guardType);
703707
}
704708

705709
/// Analyzes a collection element of the form `if (condition) ifTrue` or
@@ -1706,6 +1710,7 @@ mixin TypeAnalyzer<
17061710
flow.switchStatement_expressionEnd(null, scrutinee, expressionType);
17071711
Type? lubType;
17081712
Map<int, Error>? nonBooleanGuardErrors;
1713+
Map<int, Type>? guardTypes;
17091714
for (int i = 0; i < numCases; i++) {
17101715
// Stack: (Expression, i * ExpressionCase)
17111716
SwitchExpressionMemberInfo<Node, Expression, Variable> memberInfo =
@@ -1737,8 +1742,9 @@ mixin TypeAnalyzer<
17371742
guard = memberInfo.head.guard;
17381743
bool hasGuard = guard != null;
17391744
if (hasGuard) {
1740-
Error? nonBooleanGuardError =
1741-
_checkGuardType(guard, analyzeExpression(guard, boolType));
1745+
Type guardType = analyzeExpression(guard, boolType);
1746+
Error? nonBooleanGuardError = _checkGuardType(guard, guardType);
1747+
(guardTypes ??= {})[i] = guardType;
17421748
if (nonBooleanGuardError != null) {
17431749
(nonBooleanGuardErrors ??= {})[i] = nonBooleanGuardError;
17441750
}
@@ -1778,6 +1784,7 @@ mixin TypeAnalyzer<
17781784
return new SwitchExpressionResult(
17791785
type: lubType,
17801786
nonBooleanGuardErrors: nonBooleanGuardErrors,
1787+
guardTypes: guardTypes,
17811788
nonExhaustiveSwitchError: nonExhaustiveSwitchError);
17821789
}
17831790

@@ -1796,6 +1803,7 @@ mixin TypeAnalyzer<
17961803
bool lastCaseTerminates = true;
17971804
Map<int, Error>? switchCaseCompletesNormallyErrors;
17981805
Map<int, Map<int, Error>>? nonBooleanGuardErrors;
1806+
Map<int, Map<int, Type>>? guardTypes;
17991807
for (int caseIndex = 0; caseIndex < numCases; caseIndex++) {
18001808
// Stack: (Expression, numExecutionPaths * StatementCase)
18011809
flow.switchStatement_beginAlternatives();
@@ -1835,8 +1843,9 @@ mixin TypeAnalyzer<
18351843
// numHeads * CaseHead, Pattern),
18361844
guard = head.guard;
18371845
if (guard != null) {
1838-
Error? nonBooleanGuardError =
1839-
_checkGuardType(guard, analyzeExpression(guard, boolType));
1846+
Type guardType = analyzeExpression(guard, boolType);
1847+
Error? nonBooleanGuardError = _checkGuardType(guard, guardType);
1848+
((guardTypes ??= {})[caseIndex] ??= {})[headIndex] = guardType;
18401849
if (nonBooleanGuardError != null) {
18411850
((nonBooleanGuardErrors ??= {})[caseIndex] ??= {})[headIndex] =
18421851
nonBooleanGuardError;
@@ -1921,6 +1930,7 @@ mixin TypeAnalyzer<
19211930
scrutineeType: scrutineeType,
19221931
switchCaseCompletesNormallyErrors: switchCaseCompletesNormallyErrors,
19231932
nonBooleanGuardErrors: nonBooleanGuardErrors,
1933+
guardTypes: guardTypes,
19241934
nonExhaustiveSwitchError: nonExhaustiveSwitchError,
19251935
);
19261936
}

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,9 +1951,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
19511951
InvalidExpression? guardError = analysisResult.nonBooleanGuardError;
19521952
if (guardError != null) {
19531953
node.patternGuard.guard = guardError..parent = node.patternGuard;
1954-
} else if (!identical(node.patternGuard.guard, rewrite)) {
1955-
node.patternGuard.guard = (rewrite as Expression)
1956-
..parent = node.patternGuard;
1954+
} else {
1955+
if (!identical(node.patternGuard.guard, rewrite)) {
1956+
node.patternGuard.guard = (rewrite as Expression)
1957+
..parent = node.patternGuard;
1958+
}
1959+
if (analysisResult.guardType is DynamicType) {
1960+
node.patternGuard.guard = _createImplicitAs(
1961+
node.patternGuard.guard!.fileOffset,
1962+
node.patternGuard.guard!,
1963+
coreTypes.boolNonNullableRawType)
1964+
..parent = node.patternGuard;
1965+
}
19571966
}
19581967
rewrite = popRewrite();
19591968
if (!identical(node.patternGuard.pattern, rewrite)) {
@@ -8208,6 +8217,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
82088217
analysisResult.nonBooleanGuardErrors?[caseIndex];
82098218
if (guardError != null) {
82108219
patternGuard.guard = guardError..parent = patternGuard;
8220+
} else if (patternGuard.guard != null) {
8221+
if (analysisResult.guardTypes![caseIndex] is DynamicType) {
8222+
patternGuard.guard = _createImplicitAs(patternGuard.guard!.fileOffset,
8223+
patternGuard.guard!, coreTypes.boolNonNullableRawType)
8224+
..parent = patternGuard;
8225+
}
82118226
}
82128227
}
82138228

@@ -8360,6 +8375,15 @@ class InferenceVisitorImpl extends InferenceVisitorBase
83608375
analysisResult.nonBooleanGuardErrors?[caseIndex]?[headIndex];
83618376
if (guardError != null) {
83628377
patternGuard.guard = guardError..parent = patternGuard;
8378+
} else if (patternGuard.guard != null) {
8379+
if (analysisResult.guardTypes![caseIndex]![headIndex]
8380+
is DynamicType) {
8381+
patternGuard.guard = _createImplicitAs(
8382+
patternGuard.guard!.fileOffset,
8383+
patternGuard.guard!,
8384+
coreTypes.boolNonNullableRawType)
8385+
..parent = patternGuard;
8386+
}
83638387
}
83648388

83658389
Map<String, DartType> inferredVariableTypes = {

pkg/front_end/test/spell_checking_list_tests.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ blah
8282
blorp
8383
bold
8484
boo
85+
bools
8586
bots
8687
boundaries
8788
boundarykey
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+
dynamic guard() => true;
6+
7+
main() {
8+
var [a] = [5];
9+
int b;
10+
[b] = [5];
11+
if (a case == 5 when guard()) {
12+
a = 6;
13+
}
14+
var c = switch (a) {
15+
int d when guard() => d,
16+
_ => 0,
17+
};
18+
switch (b) {
19+
case int e when guard():
20+
print(a);
21+
}
22+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
static method guard() → dynamic
7+
return true;
8+
static method main() → dynamic {
9+
core::int a;
10+
{
11+
final synthesized dynamic #0#0 = <core::int>[5];
12+
if(!(#0#0{core::List<core::int>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final dynamic #t1 = a = #0#0{core::List<core::int>}.{core::List::[]}(0){(core::int) → core::int} in true)))
13+
throw new _in::ReachabilityError::•();
14+
}
15+
core::int b;
16+
block {
17+
final synthesized dynamic #1#0 = <core::int>[5];
18+
if(!(#1#0{core::List<core::int>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final dynamic #t2 = b = #1#0{core::List<core::int>}.{core::List::[]}(0){(core::int) → core::int} in true)))
19+
throw new _in::ReachabilityError::•();
20+
} =>#1#0;
21+
{
22+
final synthesized core::int #2#0 = a;
23+
if(#2#0 =={core::num::==}{(core::Object) → core::bool} #C2 && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
24+
a = 6;
25+
}
26+
}
27+
core::int c = block {
28+
core::int #t3;
29+
final synthesized core::int #3#0 = a;
30+
#L1:
31+
{
32+
{
33+
core::int d;
34+
if(#3#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t4 = d = #3#0 in true) && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
35+
#t3 = d;
36+
break #L1;
37+
}
38+
}
39+
{
40+
if(true) {
41+
#t3 = 0;
42+
break #L1;
43+
}
44+
}
45+
}
46+
} =>#t3;
47+
#L2:
48+
{
49+
final synthesized core::int #4#0 = b;
50+
{
51+
core::int e;
52+
if(#4#0 is{ForNonNullableByDefault} core::int && (let final dynamic #t5 = e = #4#0 in true) && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
53+
{
54+
core::print(a);
55+
}
56+
}
57+
}
58+
}
59+
}
60+
61+
constants {
62+
#C1 = 1
63+
#C2 = 5
64+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
static method guard() → dynamic
7+
return true;
8+
static method main() → dynamic {
9+
core::int a;
10+
{
11+
final synthesized dynamic #0#0 = core::_GrowableList::_literal1<core::int>(5);
12+
if(!(#0#0{core::List<core::int>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final core::int #t1 = a = #0#0{core::List<core::int>}.{core::List::[]}(0){(core::int) → core::int} in true)))
13+
throw new _in::ReachabilityError::•();
14+
}
15+
core::int b;
16+
block {
17+
final synthesized dynamic #1#0 = core::_GrowableList::_literal1<core::int>(5);
18+
if(!(#1#0{core::List<core::int>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final core::int #t2 = b = #1#0{core::List<core::int>}.{core::List::[]}(0){(core::int) → core::int} in true)))
19+
throw new _in::ReachabilityError::•();
20+
} =>#1#0;
21+
{
22+
final synthesized core::int #2#0 = a;
23+
if(#2#0 =={core::num::==}{(core::Object) → core::bool} #C2 && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
24+
a = 6;
25+
}
26+
}
27+
core::int c = block {
28+
core::int #t3;
29+
final synthesized core::int #3#0 = a;
30+
#L1:
31+
{
32+
{
33+
core::int d;
34+
if(#3#0 is{ForNonNullableByDefault} core::int && (let final core::int #t4 = d = #3#0 in true) && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
35+
#t3 = d;
36+
break #L1;
37+
}
38+
}
39+
{
40+
if(true) {
41+
#t3 = 0;
42+
break #L1;
43+
}
44+
}
45+
}
46+
} =>#t3;
47+
#L2:
48+
{
49+
final synthesized core::int #4#0 = b;
50+
{
51+
core::int e;
52+
if(#4#0 is{ForNonNullableByDefault} core::int && (let final core::int #t5 = e = #4#0 in true) && self::guard() as{TypeError,ForNonNullableByDefault} core::bool) {
53+
{
54+
core::print(a);
55+
}
56+
}
57+
}
58+
}
59+
}
60+
61+
constants {
62+
#C1 = 1
63+
#C2 = 5
64+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dynamic guard() => true;
2+
main() {}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dynamic guard() => true;
2+
main() {}

0 commit comments

Comments
 (0)