Skip to content

Commit e724a13

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Implement desugaring for simple switch statements
This CL doesn't cover the following features, among others. * Guard clauses. * Multiple patterns per case body. * Labels. Part of #49749 Change-Id: I8de86785c44eecbe6e2167dc7a97d9281249af3f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/276524 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent b7d998d commit e724a13

File tree

725 files changed

+20945
-2895
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

725 files changed

+20945
-2895
lines changed

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7248,6 +7248,11 @@ class BodyBuilder extends StackListenerImpl
72487248
} else if (labelExpressionOrPattern is PatternGuard) {
72497249
expressionOrPatterns[expressionOrPatternIndex++] =
72507250
labelExpressionOrPattern;
7251+
for (VariableDeclaration variable
7252+
in labelExpressionOrPattern.pattern.declaredVariables) {
7253+
declareVariable(variable, scope);
7254+
typeInferrer.assignedVariables.declare(variable);
7255+
}
72517256
containsPatterns = true;
72527257
} else {
72537258
expressionOrPatterns[expressionOrPatternIndex++] =

pkg/front_end/lib/src/fasta/kernel/forest.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,14 @@ class Forest {
476476
..fileOffset = fileOffset;
477477
}
478478

479+
/// Return a representation of a `switch` statement.
480+
Statement createSwitchStatement(
481+
int fileOffset, Expression expression, List<SwitchCase> cases) {
482+
// ignore: unnecessary_null_comparison
483+
assert(fileOffset != null);
484+
return new SwitchStatement(expression, cases)..fileOffset = fileOffset;
485+
}
486+
479487
/// Return a representation of an `is` expression at the given [fileOffset].
480488
/// The [operand] is the representation of the left operand. The [type] is a
481489
/// representation of the type that is the right operand. If [notFileOffset]

pkg/front_end/lib/src/fasta/kernel/internal_ast.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,10 +424,23 @@ class PatternSwitchCase extends TreeNode
424424
throw new UnimplementedError('PatternSwitchCase.expressionOffsets');
425425
}
426426

427-
class PatternSwitchStatement extends InternalStatement {
427+
class PatternSwitchStatement extends InternalStatement
428+
implements SwitchStatement {
429+
@override
428430
Expression expression;
431+
432+
@override
429433
final List<SwitchCase> cases;
430434

435+
@override
436+
bool isExplicitlyExhaustive = false;
437+
438+
@override
439+
bool get hasDefault => throw new UnimplementedError();
440+
441+
@override
442+
bool get isExhaustive => throw new UnimplementedError();
443+
431444
PatternSwitchStatement(int fileOffset, this.expression, this.cases) {
432445
this.fileOffset = fileOffset;
433446
expression.parent = this;

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

Lines changed: 235 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,14 +1761,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
17611761
VariableDeclaration matchedExpressionVariable = engine.forest
17621762
.createVariableDeclarationForValue(node.expression,
17631763
type: scrutineeType);
1764-
PatternTransformationResult transformationResult = node.patternGuard.pattern
1765-
.transform(
1766-
engine.forest
1767-
.createVariableGet(node.fileOffset, matchedExpressionVariable),
1768-
scrutineeType,
1769-
engine.forest
1770-
.createVariableGet(node.fileOffset, matchedExpressionVariable),
1771-
this);
17721764

17731765
// isPatternMatchingFailed: bool VAR = true;
17741766
VariableDeclaration isPatternMatchingFailed = engine.forest
@@ -1816,6 +1808,32 @@ class InferenceVisitorImpl extends InferenceVisitorBase
18161808
];
18171809
}
18181810

1811+
PatternTransformationResult transformationResult = node.patternGuard.pattern
1812+
.transform(
1813+
engine.forest
1814+
.createVariableGet(node.fileOffset, matchedExpressionVariable),
1815+
scrutineeType,
1816+
engine.forest
1817+
.createVariableGet(node.fileOffset, matchedExpressionVariable),
1818+
this);
1819+
replacementStatements = _transformationResultToStatements(
1820+
node.fileOffset, transformationResult, replacementStatements);
1821+
1822+
replacementStatements = [
1823+
matchedExpressionVariable,
1824+
if (otherwise != null) isPatternMatchingFailed,
1825+
...replacementStatements,
1826+
if (otherwiseBranchAsStatement != null) otherwiseBranchAsStatement
1827+
];
1828+
1829+
return new StatementInferenceResult.multiple(
1830+
node.fileOffset, replacementStatements);
1831+
}
1832+
1833+
List<Statement> _transformationResultToStatements(
1834+
int fileOffset,
1835+
PatternTransformationResult transformationResult,
1836+
List<Statement> replacementStatements) {
18191837
List<ContinuationStackElement> continuationStack = [];
18201838
for (int i = transformationResult.elements.length - 1; i >= 0; i--) {
18211839
PatternTransformationElement transformationElement =
@@ -1831,10 +1849,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
18311849
if (condition != null) {
18321850
replacementStatements = [
18331851
engine.forest.createIfStatement(
1834-
node.fileOffset,
1852+
fileOffset,
18351853
condition,
18361854
engine.forest.createBlock(
1837-
node.fileOffset, node.fileOffset, replacementStatements),
1855+
fileOffset, fileOffset, replacementStatements),
18381856
null)
18391857
];
18401858
}
@@ -1870,16 +1888,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
18701888
}
18711889
assert(continuationStack.isEmpty,
18721890
"Continuation stack is not empty at the end of desugaring.");
1873-
1874-
replacementStatements = [
1875-
matchedExpressionVariable,
1876-
if (otherwise != null) isPatternMatchingFailed,
1877-
...replacementStatements,
1878-
if (otherwiseBranchAsStatement != null) otherwiseBranchAsStatement
1879-
];
1880-
1881-
return new StatementInferenceResult.multiple(
1882-
node.fileOffset, replacementStatements);
1891+
return replacementStatements;
18831892
}
18841893

18851894
ExpressionInferenceResult visitIntJudgment(
@@ -7721,8 +7730,158 @@ class InferenceVisitorImpl extends InferenceVisitorBase
77217730

77227731
StatementInferenceResult visitPatternSwitchStatement(
77237732
PatternSwitchStatement node) {
7724-
return new StatementInferenceResult.single(
7725-
new ExpressionStatement(new InvalidExpression('$node')));
7733+
Expression expression = node.expression;
7734+
SwitchStatementTypeAnalysisResult<DartType> analysisResult =
7735+
analyzeSwitchStatement(node, expression, node.cases.length);
7736+
DartType scrutineeType = analysisResult.scrutineeType;
7737+
// Stack: (Expression)
7738+
Node? rewrite = popRewrite();
7739+
if (!identical(expression, rewrite)) {
7740+
expression = rewrite as Expression;
7741+
}
7742+
7743+
// matchedExpressionVariable: `scrutineeType` MVAR = `node.expression`;
7744+
VariableDeclaration matchedExpressionVariable = engine.forest
7745+
.createVariableDeclarationForValue(node.expression,
7746+
type: scrutineeType);
7747+
7748+
// matchResultVariable: int RVAR = -1;
7749+
VariableDeclaration matchResultVariable = engine.forest
7750+
.createVariableDeclarationForValue(
7751+
engine.forest.createIntLiteral(node.fileOffset, -1),
7752+
type: coreTypes.intNonNullableRawType);
7753+
7754+
// matchSucceeded: bool SVAR = false;
7755+
VariableDeclaration matchSucceeded = engine.forest
7756+
.createVariableDeclarationForValue(
7757+
engine.forest.createBoolLiteral(node.fileOffset, false),
7758+
type: coreTypes.boolNonNullableRawType);
7759+
7760+
List<Statement> replacementStatements = [
7761+
matchedExpressionVariable,
7762+
matchResultVariable,
7763+
matchSucceeded
7764+
];
7765+
7766+
List<SwitchCase> replacementCases = [];
7767+
7768+
List<VariableDeclaration> declaredVariableHelpers = [];
7769+
7770+
for (int caseIndex = 0; caseIndex < node.cases.length; caseIndex++) {
7771+
SwitchCase switchCase = node.cases[caseIndex];
7772+
if (switchCase is PatternSwitchCase) {
7773+
if (switchCase.patternGuards.length != 1) {
7774+
// TODO(cstefantsova): Handle multiple patternGuards.
7775+
}
7776+
Pattern pattern = switchCase.patternGuards.first.pattern;
7777+
Statement body = switchCase.body;
7778+
7779+
// setMatchResult: `matchResultVariable` = `caseIndex`;
7780+
// ==> RVAR = `caseIndex`;
7781+
Statement setMatchResult = engine.forest.createExpressionStatement(
7782+
node.fileOffset,
7783+
engine.forest.createVariableSet(
7784+
node.fileOffset,
7785+
matchResultVariable,
7786+
engine.forest.createIntLiteral(node.fileOffset, caseIndex)));
7787+
7788+
// setMatchSucceeded: `matchSucceeded` = true;
7789+
// ==> SVAR = true;
7790+
Statement setMatchSucceeded = engine.forest.createExpressionStatement(
7791+
node.fileOffset,
7792+
engine.forest.createVariableSet(node.fileOffset, matchSucceeded,
7793+
engine.forest.createBoolLiteral(node.fileOffset, true)));
7794+
7795+
PatternTransformationResult transformationResult = pattern.transform(
7796+
engine.forest
7797+
.createVariableGet(node.fileOffset, matchedExpressionVariable),
7798+
scrutineeType,
7799+
engine.forest
7800+
.createVariableGet(node.fileOffset, matchedExpressionVariable),
7801+
this);
7802+
7803+
// condition: `matchSucceeded`!
7804+
// ==> SVAR!
7805+
transformationResult = transformationResult.prependElement(
7806+
new PatternTransformationElement(
7807+
condition: engine.forest.createNot(
7808+
node.fileOffset,
7809+
engine.forest
7810+
.createVariableGet(node.fileOffset, matchSucceeded)),
7811+
variableInitializers: [],
7812+
kind: PatternTransformationElementKind.regular),
7813+
this);
7814+
7815+
List<VariableDeclaration> caseDeclaredVariableHelpers = [];
7816+
List<Statement> caseDeclaredVariableHelperInitializers = [];
7817+
for (VariableDeclaration declaredVariable
7818+
in pattern.declaredVariables) {
7819+
// declaredVariableHelper: dynamic HVAR;
7820+
VariableDeclaration declaredVariableHelper = engine.forest
7821+
.createVariableDeclaration(node.fileOffset, null,
7822+
type: const DynamicType());
7823+
7824+
caseDeclaredVariableHelpers.add(declaredVariableHelper);
7825+
7826+
// initializer:
7827+
// `declaredVariableHelper` = `declaredVariable.initializer`;
7828+
// ==> HVAR = `declaredVariable.initializer`;
7829+
caseDeclaredVariableHelperInitializers.add(engine.forest
7830+
.createExpressionStatement(
7831+
node.fileOffset,
7832+
engine.forest.createVariableSet(node.fileOffset,
7833+
declaredVariableHelper, declaredVariable.initializer!)));
7834+
7835+
// `declaredVariable`:
7836+
// declaredVariable` =
7837+
// `declaredVariableHelper`{`declaredVariable.type`}
7838+
// ==> `declaredVariable` = HVAR{`declaredVariable.type`}
7839+
declaredVariable.initializer = engine.forest
7840+
.createVariableGet(node.fileOffset, declaredVariableHelper)
7841+
..promotedType = declaredVariable.type
7842+
..parent = declaredVariable;
7843+
}
7844+
7845+
List<Statement> caseReplacementStatements = [
7846+
setMatchResult,
7847+
setMatchSucceeded,
7848+
...caseDeclaredVariableHelperInitializers
7849+
];
7850+
7851+
declaredVariableHelpers.addAll(caseDeclaredVariableHelpers);
7852+
7853+
caseReplacementStatements = _transformationResultToStatements(
7854+
node.fileOffset, transformationResult, caseReplacementStatements);
7855+
7856+
replacementStatements.addAll(caseReplacementStatements);
7857+
7858+
replacementCases.add(new SwitchCaseImpl(
7859+
[engine.forest.createIntLiteral(node.fileOffset, caseIndex)],
7860+
[node.fileOffset],
7861+
engine.forest
7862+
.createBlock(node.fileOffset, node.fileOffset, <Statement>[
7863+
...pattern.declaredVariables,
7864+
if (body is! Block || body.statements.isNotEmpty) body
7865+
]),
7866+
hasLabel: false));
7867+
} else if (switchCase is SwitchCaseImpl && switchCase.isDefault) {
7868+
replacementCases.add(switchCase);
7869+
} else {
7870+
// TODO(cstefantsova): Report an internal or compile-time error.
7871+
}
7872+
}
7873+
7874+
replacementStatements = [
7875+
...declaredVariableHelpers,
7876+
...replacementStatements,
7877+
engine.forest.createSwitchStatement(
7878+
node.fileOffset,
7879+
engine.forest.createVariableGet(node.fileOffset, matchResultVariable),
7880+
replacementCases)
7881+
];
7882+
7883+
return new StatementInferenceResult.multiple(
7884+
node.fileOffset, replacementStatements);
77267885
}
77277886

77287887
@override
@@ -8697,23 +8856,42 @@ class InferenceVisitorImpl extends InferenceVisitorBase
86978856
SwitchStatementMemberInfo<Node, Statement, Expression, VariableDeclaration>
86988857
getSwitchStatementMemberInfo(
86998858
covariant SwitchStatement node, int caseIndex) {
8700-
SwitchCaseImpl case_ = node.cases[caseIndex] as SwitchCaseImpl;
8701-
return new SwitchStatementMemberInfo([
8702-
for (Expression expression in case_.expressions)
8703-
new CaseHeadOrDefaultInfo(
8704-
pattern: expression,
8705-
// TODO(scheglov) pass actual variables, not just `{}`.
8706-
variables: {},
8707-
),
8708-
if (case_.isDefault)
8709-
new CaseHeadOrDefaultInfo(
8710-
pattern: null,
8711-
variables: {},
8712-
)
8713-
], [
8714-
case_.body
8715-
], {}, hasLabels: case_.hasLabel);
8716-
// TODO(scheglov) pass actual variables, not just `{}`.
8859+
SwitchCase case_ = node.cases[caseIndex];
8860+
if (case_ is SwitchCaseImpl) {
8861+
return new SwitchStatementMemberInfo([
8862+
for (Expression expression in case_.expressions)
8863+
new CaseHeadOrDefaultInfo(
8864+
pattern: expression,
8865+
// TODO(scheglov) pass actual variables, not just `{}`.
8866+
variables: {},
8867+
),
8868+
if (case_.isDefault)
8869+
new CaseHeadOrDefaultInfo(
8870+
pattern: null,
8871+
variables: {},
8872+
)
8873+
], [
8874+
case_.body
8875+
], {}, hasLabels: case_.hasLabel);
8876+
// TODO(scheglov) pass actual variables, not just `{}`.
8877+
} else {
8878+
case_ as PatternSwitchCase;
8879+
return new SwitchStatementMemberInfo([
8880+
for (PatternGuard patternGuard in case_.patternGuards)
8881+
new CaseHeadOrDefaultInfo(
8882+
pattern: patternGuard.pattern,
8883+
// TODO(cstefantsova): Pass actual variables, not just `{}`.
8884+
variables: {},
8885+
),
8886+
if (case_.isDefault)
8887+
new CaseHeadOrDefaultInfo(
8888+
pattern: null,
8889+
variables: {},
8890+
)
8891+
], [
8892+
case_.body
8893+
], {}, hasLabels: case_.hasLabel);
8894+
}
87178895
}
87188896

87198897
@override
@@ -8722,21 +8900,25 @@ class InferenceVisitorImpl extends InferenceVisitorBase
87228900
// Stack: (Pattern, Expression)
87238901
popRewrite(); // "when" expression
87248902
// Stack: (Pattern)
8725-
SwitchCaseImpl case_ = node.cases[caseIndex] as SwitchCaseImpl;
8726-
Expression expression = case_.expressions[subIndex];
8727-
Node? rewrite = popRewrite();
8728-
// Stack: ()
8729-
if (!identical(expression, rewrite)) {
8730-
expression = rewrite as Expression;
8731-
case_.expressions[subIndex] = expression..parent = case_;
8732-
}
8733-
Set<Field?>? enumFields = _enumFields;
8734-
if (enumFields != null) {
8735-
if (expression is StaticGet) {
8736-
enumFields.remove(expression.target);
8737-
} else if (expression is NullLiteral) {
8738-
enumFields.remove(null);
8903+
SwitchCase case_ = node.cases[caseIndex];
8904+
if (case_ is SwitchCaseImpl) {
8905+
Expression expression = case_.expressions[subIndex];
8906+
Node? rewrite = popRewrite();
8907+
// Stack: ()
8908+
if (!identical(expression, rewrite)) {
8909+
expression = rewrite as Expression;
8910+
case_.expressions[subIndex] = expression..parent = case_;
87398911
}
8912+
Set<Field?>? enumFields = _enumFields;
8913+
if (enumFields != null) {
8914+
if (expression is StaticGet) {
8915+
enumFields.remove(expression.target);
8916+
} else if (expression is NullLiteral) {
8917+
enumFields.remove(null);
8918+
}
8919+
}
8920+
} else {
8921+
case_ as PatternSwitchCase;
87408922
}
87418923
}
87428924

0 commit comments

Comments
 (0)