mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[cfe] Provide pre-desugaring node to flow analysis to fix promotion
Part of https://github.com/dart-lang/sdk/issues/49749 Change-Id: I2a70518975c809f28f0d0f72f7365492ca7d6e83 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287601 Commit-Queue: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
c974c70f31
commit
b16acf4f35
|
@ -1744,7 +1744,7 @@ mixin TypeAnalyzer<
|
|||
handleNoGuard(node, i);
|
||||
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
|
||||
}
|
||||
handleCaseHead(node, caseIndex: i, subIndex: 0);
|
||||
handleCaseHead(node, memberInfo.head, caseIndex: i, subIndex: 0);
|
||||
} else {
|
||||
handleDefault(node, caseIndex: i, subIndex: 0);
|
||||
}
|
||||
|
@ -1843,7 +1843,9 @@ mixin TypeAnalyzer<
|
|||
} else {
|
||||
handleNoGuard(node, caseIndex);
|
||||
}
|
||||
handleCaseHead(node, caseIndex: caseIndex, subIndex: headIndex);
|
||||
head = handleCaseHead(node, head,
|
||||
caseIndex: caseIndex, subIndex: headIndex);
|
||||
guard = head.guard;
|
||||
} else {
|
||||
hasDefault = true;
|
||||
handleDefault(node, caseIndex: caseIndex, subIndex: headIndex);
|
||||
|
@ -2136,11 +2138,15 @@ mixin TypeAnalyzer<
|
|||
/// Called after visiting a single `case` clause, consisting of a pattern and
|
||||
/// an optional guard.
|
||||
///
|
||||
/// [node] is the enclosing switch statement or switch expression and
|
||||
/// [node] is the enclosing switch statement or switch expression,
|
||||
/// [head] is the head to be handled, and
|
||||
/// [caseIndex] is the index of the `case` clause.
|
||||
///
|
||||
/// Returns the updated case head.
|
||||
///
|
||||
/// Stack effect: pops (Pattern, Expression) and pushes (CaseHead).
|
||||
void handleCaseHead(Node node,
|
||||
CaseHeadOrDefaultInfo<Node, Expression, Variable> handleCaseHead(
|
||||
Node node, CaseHeadOrDefaultInfo<Node, Expression, Variable> head,
|
||||
{required int caseIndex, required int subIndex});
|
||||
|
||||
/// Called after visiting a `default` clause.
|
||||
|
|
|
@ -3727,7 +3727,8 @@ class _MiniAstTypeAnalyzer
|
|||
}
|
||||
|
||||
@override
|
||||
void handleCaseHead(Node node,
|
||||
CaseHeadOrDefaultInfo<Node, Expression, Var> handleCaseHead(
|
||||
Node node, CaseHeadOrDefaultInfo<Node, Expression, Var> head,
|
||||
{required int caseIndex, required int subIndex}) {
|
||||
Iterable<Var> variables = [];
|
||||
if (node is _SwitchExpression) {
|
||||
|
@ -3748,6 +3749,8 @@ class _MiniAstTypeAnalyzer
|
|||
_irBuilder.apply(
|
||||
'head', [Kind.pattern, Kind.expression, Kind.variables], Kind.caseHead,
|
||||
location: node.location);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
void handleDeclaredVariablePattern(covariant _VariablePattern node,
|
||||
|
|
|
@ -1085,8 +1085,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
AstNode node, int caseIndex, Iterable<PromotableElement> variables) {}
|
||||
|
||||
@override
|
||||
void handleCaseHead(
|
||||
covariant AstNodeImpl node, {
|
||||
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> handleCaseHead(
|
||||
covariant AstNodeImpl node,
|
||||
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> head, {
|
||||
required int caseIndex,
|
||||
required int subIndex,
|
||||
}) {
|
||||
|
@ -1101,6 +1102,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
legacySwitchExhaustiveness
|
||||
?.visitSwitchExpressionCase(node.cases[caseIndex]);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -9864,10 +9864,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
|||
}
|
||||
|
||||
@override
|
||||
void handleCaseHead(
|
||||
covariant /* SwitchStatement | SwitchExpression */ Object node,
|
||||
{required int caseIndex,
|
||||
required int subIndex}) {
|
||||
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration>
|
||||
handleCaseHead(
|
||||
covariant /* SwitchStatement | SwitchExpression */ Object node,
|
||||
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> head,
|
||||
{required int caseIndex,
|
||||
required int subIndex}) {
|
||||
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> result =
|
||||
head;
|
||||
int? stackBase;
|
||||
assert(checkStackBase(node as TreeNode, stackBase = stackHeight - 2));
|
||||
|
||||
|
@ -9919,6 +9923,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
|||
!identical(guardRewrite, patternGuard.guard)) {
|
||||
patternGuard.guard = (guardRewrite as Expression)
|
||||
..parent = patternGuard;
|
||||
|
||||
result = new CaseHeadOrDefaultInfo(
|
||||
pattern: head.pattern,
|
||||
guard: patternGuard.guard,
|
||||
variables: head.variables,
|
||||
);
|
||||
}
|
||||
Object? rewrite = popRewrite();
|
||||
if (!identical(rewrite, patternGuard.pattern)) {
|
||||
|
@ -9972,6 +9982,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
|||
hasGuard: patternGuard.guard != null,
|
||||
fileOffset: switchExpressionCase.fileOffset));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
test(String? x) {
|
||||
switch (x) {
|
||||
case String? foobar? when foobar is Never:
|
||||
case String? foobar when foobar != null:
|
||||
case String? foobar! when foobar == "foobar":
|
||||
return foobar.startsWith("foo"); // The static type of 'foobar' is expected to be the non-nullable 'String'.
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic {
|
||||
#L1:
|
||||
{
|
||||
core::String? foobar;
|
||||
core::String? foobar#1;
|
||||
core::String? foobar#2;
|
||||
final core::String? #0#0 = x;
|
||||
dynamic #t1;
|
||||
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)) {
|
||||
core::String? foobar = #t1{core::String?};
|
||||
{
|
||||
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic {
|
||||
#L1:
|
||||
{
|
||||
core::String? foobar;
|
||||
core::String? foobar#1;
|
||||
core::String? foobar#2;
|
||||
final core::String? #0#0 = x;
|
||||
dynamic #t1;
|
||||
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)) {
|
||||
core::String? foobar = #t1{core::String?};
|
||||
{
|
||||
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
test(String? x) {}
|
|
@ -0,0 +1 @@
|
|||
test(String? x) {}
|
|
@ -0,0 +1,25 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic {
|
||||
#L1:
|
||||
{
|
||||
core::String? foobar;
|
||||
core::String? foobar#1;
|
||||
core::String? foobar#2;
|
||||
final core::String? #0#0 = x;
|
||||
dynamic #t1;
|
||||
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)) {
|
||||
core::String? foobar = #t1{core::String?};
|
||||
{
|
||||
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic {
|
||||
#L1:
|
||||
{
|
||||
core::String? foobar;
|
||||
core::String? foobar#1;
|
||||
core::String? foobar#2;
|
||||
final core::String? #0#0 = x;
|
||||
dynamic #t1;
|
||||
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)) {
|
||||
core::String? foobar = #t1{core::String?};
|
||||
{
|
||||
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic
|
||||
;
|
|
@ -0,0 +1,25 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test(core::String? x) → dynamic {
|
||||
#L1:
|
||||
{
|
||||
core::String? foobar;
|
||||
core::String? foobar#1;
|
||||
core::String? foobar#2;
|
||||
final core::String? #0#0 = x;
|
||||
dynamic #t1;
|
||||
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)) {
|
||||
core::String? foobar = #t1{core::String?};
|
||||
{
|
||||
return foobar{core::String}.{core::String::startsWith}("foo"){(core::Pattern, [core::int]) → core::bool};
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue