Skip to content

Commit b16acf4

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Provide pre-desugaring node to flow analysis to fix promotion
Part of #49749 Change-Id: I2a70518975c809f28f0d0f72f7365492ca7d6e83 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287601 Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent c974c70 commit b16acf4

13 files changed

+182
-11
lines changed

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -1744,7 +1744,7 @@ mixin TypeAnalyzer<
17441744
handleNoGuard(node, i);
17451745
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
17461746
}
1747-
handleCaseHead(node, caseIndex: i, subIndex: 0);
1747+
handleCaseHead(node, memberInfo.head, caseIndex: i, subIndex: 0);
17481748
} else {
17491749
handleDefault(node, caseIndex: i, subIndex: 0);
17501750
}
@@ -1843,7 +1843,9 @@ mixin TypeAnalyzer<
18431843
} else {
18441844
handleNoGuard(node, caseIndex);
18451845
}
1846-
handleCaseHead(node, caseIndex: caseIndex, subIndex: headIndex);
1846+
head = handleCaseHead(node, head,
1847+
caseIndex: caseIndex, subIndex: headIndex);
1848+
guard = head.guard;
18471849
} else {
18481850
hasDefault = true;
18491851
handleDefault(node, caseIndex: caseIndex, subIndex: headIndex);
@@ -2136,11 +2138,15 @@ mixin TypeAnalyzer<
21362138
/// Called after visiting a single `case` clause, consisting of a pattern and
21372139
/// an optional guard.
21382140
///
2139-
/// [node] is the enclosing switch statement or switch expression and
2141+
/// [node] is the enclosing switch statement or switch expression,
2142+
/// [head] is the head to be handled, and
21402143
/// [caseIndex] is the index of the `case` clause.
21412144
///
2145+
/// Returns the updated case head.
2146+
///
21422147
/// Stack effect: pops (Pattern, Expression) and pushes (CaseHead).
2143-
void handleCaseHead(Node node,
2148+
CaseHeadOrDefaultInfo<Node, Expression, Variable> handleCaseHead(
2149+
Node node, CaseHeadOrDefaultInfo<Node, Expression, Variable> head,
21442150
{required int caseIndex, required int subIndex});
21452151

21462152
/// Called after visiting a `default` clause.

pkg/_fe_analyzer_shared/test/mini_ast.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -3727,7 +3727,8 @@ class _MiniAstTypeAnalyzer
37273727
}
37283728

37293729
@override
3730-
void handleCaseHead(Node node,
3730+
CaseHeadOrDefaultInfo<Node, Expression, Var> handleCaseHead(
3731+
Node node, CaseHeadOrDefaultInfo<Node, Expression, Var> head,
37313732
{required int caseIndex, required int subIndex}) {
37323733
Iterable<Var> variables = [];
37333734
if (node is _SwitchExpression) {
@@ -3748,6 +3749,8 @@ class _MiniAstTypeAnalyzer
37483749
_irBuilder.apply(
37493750
'head', [Kind.pattern, Kind.expression, Kind.variables], Kind.caseHead,
37503751
location: node.location);
3752+
3753+
return head;
37513754
}
37523755

37533756
void handleDeclaredVariablePattern(covariant _VariablePattern node,

pkg/analyzer/lib/src/generated/resolver.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -1085,8 +1085,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
10851085
AstNode node, int caseIndex, Iterable<PromotableElement> variables) {}
10861086

10871087
@override
1088-
void handleCaseHead(
1089-
covariant AstNodeImpl node, {
1088+
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> handleCaseHead(
1089+
covariant AstNodeImpl node,
1090+
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> head, {
10901091
required int caseIndex,
10911092
required int subIndex,
10921093
}) {
@@ -1101,6 +1102,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
11011102
legacySwitchExhaustiveness
11021103
?.visitSwitchExpressionCase(node.cases[caseIndex]);
11031104
}
1105+
1106+
return head;
11041107
}
11051108

11061109
@override

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

+16-4
Original file line numberDiff line numberDiff line change
@@ -9864,10 +9864,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
98649864
}
98659865

98669866
@override
9867-
void handleCaseHead(
9868-
covariant /* SwitchStatement | SwitchExpression */ Object node,
9869-
{required int caseIndex,
9870-
required int subIndex}) {
9867+
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration>
9868+
handleCaseHead(
9869+
covariant /* SwitchStatement | SwitchExpression */ Object node,
9870+
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> head,
9871+
{required int caseIndex,
9872+
required int subIndex}) {
9873+
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> result =
9874+
head;
98719875
int? stackBase;
98729876
assert(checkStackBase(node as TreeNode, stackBase = stackHeight - 2));
98739877

@@ -9919,6 +9923,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
99199923
!identical(guardRewrite, patternGuard.guard)) {
99209924
patternGuard.guard = (guardRewrite as Expression)
99219925
..parent = patternGuard;
9926+
9927+
result = new CaseHeadOrDefaultInfo(
9928+
pattern: head.pattern,
9929+
guard: patternGuard.guard,
9930+
variables: head.variables,
9931+
);
99229932
}
99239933
Object? rewrite = popRewrite();
99249934
if (!identical(rewrite, patternGuard.pattern)) {
@@ -9972,6 +9982,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
99729982
hasGuard: patternGuard.guard != null,
99739983
fileOffset: switchExpressionCase.fileOffset));
99749984
}
9985+
9986+
return result;
99759987
}
99769988

99779989
@override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
test(String? x) {
6+
switch (x) {
7+
case String? foobar? when foobar is Never:
8+
case String? foobar when foobar != null:
9+
case String? foobar! when foobar == "foobar":
10+
return foobar.startsWith("foo"); // The static type of 'foobar' is expected to be the non-nullable 'String'.
11+
default:
12+
return null;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic {
6+
#L1:
7+
{
8+
core::String? foobar;
9+
core::String? foobar#1;
10+
core::String? foobar#2;
11+
final core::String? #0#0 = x;
12+
dynamic #t1;
13+
if((!(#0#0 == null) ?{core::bool} #0#0{core::String} is{ForNonNullableByDefault} core::String? && (let final dynamic #t2 = foobar = #0#0{core::String} in true) : false) && foobar{core::String} is{ForNonNullableByDefault} Never && (let final dynamic #t3 = #t1 = foobar in true) || #0#0 is{ForNonNullableByDefault} core::String? && (let final dynamic #t4 = foobar#1 = #0#0 in true) && !(foobar#1 == null) && (let final dynamic #t5 = #t1 = foobar#1 in true) || (let final dynamic #t6 = #0#0! in #0#0! is{ForNonNullableByDefault} core::String? && (let final dynamic #t7 = foobar#2 = #0#0! in true)) && foobar#2{core::String} =={core::String::==}{(core::Object) → core::bool} "foobar" && (let final dynamic #t8 = #t1 = foobar#2 in true)) {
14+
core::String? foobar = #t1{core::String?};
15+
{
16+
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
17+
}
18+
}
19+
else {
20+
{
21+
return null;
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic {
6+
#L1:
7+
{
8+
core::String? foobar;
9+
core::String? foobar#1;
10+
core::String? foobar#2;
11+
final core::String? #0#0 = x;
12+
dynamic #t1;
13+
if((!(#0#0 == null) ?{core::bool} #0#0{core::String} is{ForNonNullableByDefault} core::String? && (let final core::String #t2 = foobar = #0#0{core::String} in true) : false) && foobar{core::String} is{ForNonNullableByDefault} Never && (let final core::String? #t3 = #t1 = foobar in true) || #0#0 is{ForNonNullableByDefault} core::String? && (let final core::String? #t4 = foobar#1 = #0#0 in true) && !(foobar#1 == null) && (let final core::String? #t5 = #t1 = foobar#1 in true) || (let final core::String? #t6 = #0#0! in #0#0! is{ForNonNullableByDefault} core::String? && (let final core::String? #t7 = foobar#2 = #0#0! in true)) && foobar#2{core::String} =={core::String::==}{(core::Object) → core::bool} "foobar" && (let final core::String? #t8 = #t1 = foobar#2 in true)) {
14+
core::String? foobar = #t1{core::String?};
15+
{
16+
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
17+
}
18+
}
19+
else {
20+
{
21+
return null;
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test(String? x) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test(String? x) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic {
6+
#L1:
7+
{
8+
core::String? foobar;
9+
core::String? foobar#1;
10+
core::String? foobar#2;
11+
final core::String? #0#0 = x;
12+
dynamic #t1;
13+
if((!(#0#0 == null) ?{core::bool} #0#0{core::String} is{ForNonNullableByDefault} core::String? && (let final dynamic #t2 = foobar = #0#0{core::String} in true) : false) && foobar{core::String} is{ForNonNullableByDefault} Never && (let final dynamic #t3 = #t1 = foobar in true) || #0#0 is{ForNonNullableByDefault} core::String? && (let final dynamic #t4 = foobar#1 = #0#0 in true) && !(foobar#1 == null) && (let final dynamic #t5 = #t1 = foobar#1 in true) || (let final dynamic #t6 = #0#0! in #0#0! is{ForNonNullableByDefault} core::String? && (let final dynamic #t7 = foobar#2 = #0#0! in true)) && foobar#2{core::String} =={core::String::==}{(core::Object) → core::bool} "foobar" && (let final dynamic #t8 = #t1 = foobar#2 in true)) {
14+
core::String? foobar = #t1{core::String?};
15+
{
16+
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
17+
}
18+
}
19+
else {
20+
{
21+
return null;
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic {
6+
#L1:
7+
{
8+
core::String? foobar;
9+
core::String? foobar#1;
10+
core::String? foobar#2;
11+
final core::String? #0#0 = x;
12+
dynamic #t1;
13+
if((!(#0#0 == null) ?{core::bool} #0#0{core::String} is{ForNonNullableByDefault} core::String? && (let final dynamic #t2 = foobar = #0#0{core::String} in true) : false) && foobar{core::String} is{ForNonNullableByDefault} Never && (let final dynamic #t3 = #t1 = foobar in true) || #0#0 is{ForNonNullableByDefault} core::String? && (let final dynamic #t4 = foobar#1 = #0#0 in true) && !(foobar#1 == null) && (let final dynamic #t5 = #t1 = foobar#1 in true) || (let final dynamic #t6 = #0#0! in #0#0! is{ForNonNullableByDefault} core::String? && (let final dynamic #t7 = foobar#2 = #0#0! in true)) && foobar#2{core::String} =={core::String::==}{(core::Object) → core::bool} "foobar" && (let final dynamic #t8 = #t1 = foobar#2 in true)) {
14+
core::String? foobar = #t1{core::String?};
15+
{
16+
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
17+
}
18+
}
19+
else {
20+
{
21+
return null;
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic
6+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test(core::String? x) → dynamic {
6+
#L1:
7+
{
8+
core::String? foobar;
9+
core::String? foobar#1;
10+
core::String? foobar#2;
11+
final core::String? #0#0 = x;
12+
dynamic #t1;
13+
if((!(#0#0 == null) ?{core::bool} #0#0{core::String} is{ForNonNullableByDefault} core::String? && (let final core::String #t2 = foobar = #0#0{core::String} in true) : false) && foobar{core::String} is{ForNonNullableByDefault} Never && (let final core::String? #t3 = #t1 = foobar in true) || #0#0 is{ForNonNullableByDefault} core::String? && (let final core::String? #t4 = foobar#1 = #0#0 in true) && !(foobar#1 == null) && (let final core::String? #t5 = #t1 = foobar#1 in true) || (let final core::String? #t6 = #0#0! in #0#0! is{ForNonNullableByDefault} core::String? && (let final core::String? #t7 = foobar#2 = #0#0! in true)) && foobar#2{core::String} =={core::String::==}{(core::Object) → core::bool} "foobar" && (let final core::String? #t8 = #t1 = foobar#2 in true)) {
14+
core::String? foobar = #t1{core::String?};
15+
{
16+
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
17+
}
18+
}
19+
else {
20+
{
21+
return null;
22+
}
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)