mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:29:48 +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);
|
handleNoGuard(node, i);
|
||||||
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
|
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
|
||||||
}
|
}
|
||||||
handleCaseHead(node, caseIndex: i, subIndex: 0);
|
handleCaseHead(node, memberInfo.head, caseIndex: i, subIndex: 0);
|
||||||
} else {
|
} else {
|
||||||
handleDefault(node, caseIndex: i, subIndex: 0);
|
handleDefault(node, caseIndex: i, subIndex: 0);
|
||||||
}
|
}
|
||||||
|
@ -1843,7 +1843,9 @@ mixin TypeAnalyzer<
|
||||||
} else {
|
} else {
|
||||||
handleNoGuard(node, caseIndex);
|
handleNoGuard(node, caseIndex);
|
||||||
}
|
}
|
||||||
handleCaseHead(node, caseIndex: caseIndex, subIndex: headIndex);
|
head = handleCaseHead(node, head,
|
||||||
|
caseIndex: caseIndex, subIndex: headIndex);
|
||||||
|
guard = head.guard;
|
||||||
} else {
|
} else {
|
||||||
hasDefault = true;
|
hasDefault = true;
|
||||||
handleDefault(node, caseIndex: caseIndex, subIndex: headIndex);
|
handleDefault(node, caseIndex: caseIndex, subIndex: headIndex);
|
||||||
|
@ -2136,11 +2138,15 @@ mixin TypeAnalyzer<
|
||||||
/// Called after visiting a single `case` clause, consisting of a pattern and
|
/// Called after visiting a single `case` clause, consisting of a pattern and
|
||||||
/// an optional guard.
|
/// 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.
|
/// [caseIndex] is the index of the `case` clause.
|
||||||
///
|
///
|
||||||
|
/// Returns the updated case head.
|
||||||
|
///
|
||||||
/// Stack effect: pops (Pattern, Expression) and pushes (CaseHead).
|
/// 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});
|
{required int caseIndex, required int subIndex});
|
||||||
|
|
||||||
/// Called after visiting a `default` clause.
|
/// Called after visiting a `default` clause.
|
||||||
|
|
|
@ -3727,7 +3727,8 @@ class _MiniAstTypeAnalyzer
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleCaseHead(Node node,
|
CaseHeadOrDefaultInfo<Node, Expression, Var> handleCaseHead(
|
||||||
|
Node node, CaseHeadOrDefaultInfo<Node, Expression, Var> head,
|
||||||
{required int caseIndex, required int subIndex}) {
|
{required int caseIndex, required int subIndex}) {
|
||||||
Iterable<Var> variables = [];
|
Iterable<Var> variables = [];
|
||||||
if (node is _SwitchExpression) {
|
if (node is _SwitchExpression) {
|
||||||
|
@ -3748,6 +3749,8 @@ class _MiniAstTypeAnalyzer
|
||||||
_irBuilder.apply(
|
_irBuilder.apply(
|
||||||
'head', [Kind.pattern, Kind.expression, Kind.variables], Kind.caseHead,
|
'head', [Kind.pattern, Kind.expression, Kind.variables], Kind.caseHead,
|
||||||
location: node.location);
|
location: node.location);
|
||||||
|
|
||||||
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeclaredVariablePattern(covariant _VariablePattern node,
|
void handleDeclaredVariablePattern(covariant _VariablePattern node,
|
||||||
|
|
|
@ -1085,8 +1085,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||||
AstNode node, int caseIndex, Iterable<PromotableElement> variables) {}
|
AstNode node, int caseIndex, Iterable<PromotableElement> variables) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleCaseHead(
|
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> handleCaseHead(
|
||||||
covariant AstNodeImpl node, {
|
covariant AstNodeImpl node,
|
||||||
|
CaseHeadOrDefaultInfo<AstNode, Expression, PromotableElement> head, {
|
||||||
required int caseIndex,
|
required int caseIndex,
|
||||||
required int subIndex,
|
required int subIndex,
|
||||||
}) {
|
}) {
|
||||||
|
@ -1101,6 +1102,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||||
legacySwitchExhaustiveness
|
legacySwitchExhaustiveness
|
||||||
?.visitSwitchExpressionCase(node.cases[caseIndex]);
|
?.visitSwitchExpressionCase(node.cases[caseIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -9864,10 +9864,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleCaseHead(
|
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration>
|
||||||
covariant /* SwitchStatement | SwitchExpression */ Object node,
|
handleCaseHead(
|
||||||
{required int caseIndex,
|
covariant /* SwitchStatement | SwitchExpression */ Object node,
|
||||||
required int subIndex}) {
|
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> head,
|
||||||
|
{required int caseIndex,
|
||||||
|
required int subIndex}) {
|
||||||
|
CaseHeadOrDefaultInfo<TreeNode, Expression, VariableDeclaration> result =
|
||||||
|
head;
|
||||||
int? stackBase;
|
int? stackBase;
|
||||||
assert(checkStackBase(node as TreeNode, stackBase = stackHeight - 2));
|
assert(checkStackBase(node as TreeNode, stackBase = stackHeight - 2));
|
||||||
|
|
||||||
|
@ -9919,6 +9923,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||||
!identical(guardRewrite, patternGuard.guard)) {
|
!identical(guardRewrite, patternGuard.guard)) {
|
||||||
patternGuard.guard = (guardRewrite as Expression)
|
patternGuard.guard = (guardRewrite as Expression)
|
||||||
..parent = patternGuard;
|
..parent = patternGuard;
|
||||||
|
|
||||||
|
result = new CaseHeadOrDefaultInfo(
|
||||||
|
pattern: head.pattern,
|
||||||
|
guard: patternGuard.guard,
|
||||||
|
variables: head.variables,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Object? rewrite = popRewrite();
|
Object? rewrite = popRewrite();
|
||||||
if (!identical(rewrite, patternGuard.pattern)) {
|
if (!identical(rewrite, patternGuard.pattern)) {
|
||||||
|
@ -9972,6 +9982,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||||
hasGuard: patternGuard.guard != null,
|
hasGuard: patternGuard.guard != null,
|
||||||
fileOffset: switchExpressionCase.fileOffset));
|
fileOffset: switchExpressionCase.fileOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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