Skip to content

Commit f7941eb

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Handle break/continue in flow analysis
Change-Id: Ic02e60237b8eda3c98c0715e3fcc668d21e1bf62 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/126728 Reviewed-by: Dmitry Stefantsov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 175ceca commit f7941eb

File tree

7 files changed

+105
-49
lines changed

7 files changed

+105
-49
lines changed

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

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,8 +2402,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
24022402
exitLocalScope();
24032403
push(block);
24042404
if (blockKind == BlockKind.tryStatement) {
2405-
// This is matched by the calls to [deferNode] and [endNode] in
2406-
// [endTryStatement].
2405+
// This is matched by the call to [beginNode] in [beginBlock].
24072406
typeInferrer?.assignedVariables?.endNode(block);
24082407
}
24092408
}
@@ -2624,23 +2623,30 @@ class BodyBuilder extends ScopeListener<JumpTarget>
26242623
exitLocalScope();
26252624
JumpTarget continueTarget = exitContinueTarget();
26262625
JumpTarget breakTarget = exitBreakTarget();
2626+
List<BreakStatementImpl> continueStatements;
26272627
if (continueTarget.hasUsers) {
26282628
body = forest.createLabeledStatement(body);
2629-
continueTarget.resolveContinues(forest, body);
2629+
continueStatements = continueTarget.resolveContinues(forest, body);
26302630
}
26312631
Expression condition;
26322632
if (conditionStatement is ExpressionStatement) {
26332633
condition = conditionStatement.expression;
26342634
} else {
26352635
assert(conditionStatement is EmptyStatement);
26362636
}
2637-
Statement result = forest.createForStatement(
2637+
Statement forStatement = forest.createForStatement(
26382638
offsetForToken(forKeyword), variables, condition, updates, body);
26392639
typeInferrer?.assignedVariables
2640-
?.storeInfo(result, assignedVariablesNodeInfo);
2640+
?.storeInfo(forStatement, assignedVariablesNodeInfo);
2641+
if (continueStatements != null) {
2642+
for (BreakStatementImpl continueStatement in continueStatements) {
2643+
continueStatement.targetStatement = forStatement;
2644+
}
2645+
}
2646+
Statement result = forStatement;
26412647
if (breakTarget.hasUsers) {
26422648
result = forest.createLabeledStatement(result);
2643-
breakTarget.resolveBreaks(forest, result);
2649+
breakTarget.resolveBreaks(forest, result, forStatement);
26442650
}
26452651
if (variableOrExpression is ParserRecovery) {
26462652
problemInLoopOrSwitch ??= buildProblemStatement(
@@ -4397,17 +4403,24 @@ class BodyBuilder extends ScopeListener<JumpTarget>
43974403
Statement body = popStatement();
43984404
JumpTarget continueTarget = exitContinueTarget();
43994405
JumpTarget breakTarget = exitBreakTarget();
4406+
List<BreakStatementImpl> continueStatements;
44004407
if (continueTarget.hasUsers) {
44014408
body = forest.createLabeledStatement(body);
4402-
continueTarget.resolveContinues(forest, body);
4409+
continueStatements = continueTarget.resolveContinues(forest, body);
44034410
}
4404-
Statement result =
4411+
Statement doStatement =
44054412
forest.createDoStatement(offsetForToken(doKeyword), body, condition);
44064413
// This is matched by the [beginNode] call in [beginDoWhileStatement].
4407-
typeInferrer?.assignedVariables?.endNode(result);
4414+
typeInferrer?.assignedVariables?.endNode(doStatement);
4415+
if (continueStatements != null) {
4416+
for (BreakStatementImpl continueStatement in continueStatements) {
4417+
continueStatement.targetStatement = doStatement;
4418+
}
4419+
}
4420+
Statement result = doStatement;
44084421
if (breakTarget.hasUsers) {
44094422
result = forest.createLabeledStatement(result);
4410-
breakTarget.resolveBreaks(forest, result);
4423+
breakTarget.resolveBreaks(forest, result, doStatement);
44114424
}
44124425
exitLoopOrSwitch(result);
44134426
}
@@ -4584,9 +4597,10 @@ class BodyBuilder extends ScopeListener<JumpTarget>
45844597
exitLocalScope();
45854598
JumpTarget continueTarget = exitContinueTarget();
45864599
JumpTarget breakTarget = exitBreakTarget();
4600+
List<BreakStatementImpl> continueStatements;
45874601
if (continueTarget.hasUsers) {
45884602
body = forest.createLabeledStatement(body);
4589-
continueTarget.resolveContinues(forest, body);
4603+
continueStatements = continueTarget.resolveContinues(forest, body);
45904604
}
45914605
VariableDeclaration variable = buildForInVariable(forToken, lvalue);
45924606
Expression problem = checkForInVariable(lvalue, variable, forToken);
@@ -4605,15 +4619,21 @@ class BodyBuilder extends ScopeListener<JumpTarget>
46054619
body = combineStatements(prologue, body);
46064620
}
46074621
}
4608-
Statement result = new ForInStatement(variable, expression, body,
4622+
Statement forInStatement = new ForInStatement(variable, expression, body,
46094623
isAsync: awaitToken != null)
46104624
..fileOffset = awaitToken?.charOffset ?? forToken.charOffset
46114625
..bodyOffset = body.fileOffset; // TODO(ahe): Isn't this redundant?
46124626
typeInferrer?.assignedVariables
4613-
?.storeInfo(result, assignedVariablesNodeInfo);
4627+
?.storeInfo(forInStatement, assignedVariablesNodeInfo);
4628+
if (continueStatements != null) {
4629+
for (BreakStatementImpl continueStatement in continueStatements) {
4630+
continueStatement.targetStatement = forInStatement;
4631+
}
4632+
}
4633+
Statement result = forInStatement;
46144634
if (breakTarget.hasUsers) {
46154635
result = forest.createLabeledStatement(result);
4616-
breakTarget.resolveBreaks(forest, result);
4636+
breakTarget.resolveBreaks(forest, result, forInStatement);
46174637
}
46184638
if (problem != null) {
46194639
result = combineStatements(
@@ -4661,8 +4681,14 @@ class BodyBuilder extends ScopeListener<JumpTarget>
46614681
if (statement is! LabeledStatement) {
46624682
statement = forest.createLabeledStatement(statement);
46634683
}
4664-
target.breakTarget.resolveBreaks(forest, statement);
4665-
target.continueTarget.resolveContinues(forest, statement);
4684+
target.breakTarget.resolveBreaks(forest, statement, statement);
4685+
List<BreakStatementImpl> continueStatements =
4686+
target.continueTarget.resolveContinues(forest, statement);
4687+
if (continueStatements != null) {
4688+
for (BreakStatementImpl continueStatement in continueStatements) {
4689+
continueStatement.targetStatement = statement;
4690+
}
4691+
}
46664692
}
46674693
push(statement);
46684694
}
@@ -4700,16 +4726,22 @@ class BodyBuilder extends ScopeListener<JumpTarget>
47004726
Expression condition = popForValue();
47014727
JumpTarget continueTarget = exitContinueTarget();
47024728
JumpTarget breakTarget = exitBreakTarget();
4729+
List<BreakStatementImpl> continueStatements;
47034730
if (continueTarget.hasUsers) {
47044731
body = forest.createLabeledStatement(body);
4705-
continueTarget.resolveContinues(forest, body);
4732+
continueStatements = continueTarget.resolveContinues(forest, body);
47064733
}
47074734
Statement whileStatement = forest.createWhileStatement(
47084735
offsetForToken(whileKeyword), condition, body);
4736+
if (continueStatements != null) {
4737+
for (BreakStatementImpl continueStatement in continueStatements) {
4738+
continueStatement.targetStatement = whileStatement;
4739+
}
4740+
}
47094741
Statement result = whileStatement;
47104742
if (breakTarget.hasUsers) {
47114743
result = forest.createLabeledStatement(result);
4712-
breakTarget.resolveBreaks(forest, result);
4744+
breakTarget.resolveBreaks(forest, result, whileStatement);
47134745
}
47144746
exitLoopOrSwitch(result);
47154747
// This is matched by the [beginNode] call in [beginWhileStatement].
@@ -4899,7 +4931,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
48994931
Statement result = switchStatement;
49004932
if (target.hasUsers) {
49014933
result = forest.createLabeledStatement(result);
4902-
target.resolveBreaks(forest, result);
4934+
target.resolveBreaks(forest, result, switchStatement);
49034935
}
49044936
exitLoopOrSwitch(result);
49054937
// This is matched by the [beginNode] call in [beginSwitchBlock].
@@ -5730,20 +5762,25 @@ class JumpTarget extends BuilderImpl {
57305762
users.add(statement);
57315763
}
57325764

5733-
void resolveBreaks(Forest forest, Statement target) {
5765+
void resolveBreaks(
5766+
Forest forest, Statement target, Statement targetStatement) {
57345767
assert(isBreakTarget);
5735-
for (BreakStatement user in users) {
5768+
for (BreakStatementImpl user in users) {
57365769
user.target = target;
5770+
user.targetStatement = targetStatement;
57375771
}
57385772
users.clear();
57395773
}
57405774

5741-
void resolveContinues(Forest forest, Statement target) {
5775+
List<BreakStatementImpl> resolveContinues(Forest forest, Statement target) {
57425776
assert(isContinueTarget);
5743-
for (BreakStatement user in users) {
5777+
List<BreakStatementImpl> statements = <BreakStatementImpl>[];
5778+
for (BreakStatementImpl user in users) {
57445779
user.target = target;
5780+
statements.add(user);
57455781
}
57465782
users.clear();
5783+
return statements;
57475784
}
57485785

57495786
void resolveGotos(Forest forest, SwitchCase target) {
@@ -5804,12 +5841,13 @@ class LabelTarget extends BuilderImpl implements JumpTarget {
58045841
unsupported("addGoto", charOffset, fileUri);
58055842
}
58065843

5807-
void resolveBreaks(Forest forest, Statement target) {
5808-
breakTarget.resolveBreaks(forest, target);
5844+
void resolveBreaks(
5845+
Forest forest, Statement target, Statement targetStatement) {
5846+
breakTarget.resolveBreaks(forest, target, targetStatement);
58095847
}
58105848

5811-
void resolveContinues(Forest forest, Statement target) {
5812-
continueTarget.resolveContinues(forest, target);
5849+
List<BreakStatementImpl> resolveContinues(Forest forest, Statement target) {
5850+
return continueTarget.resolveContinues(forest, target);
58135851
}
58145852

58155853
void resolveGotos(Forest forest, SwitchCase target) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ class Forest {
323323
Statement createBreakStatement(int fileOffset, Object label) {
324324
assert(fileOffset != null);
325325
// TODO(johnniwinther): Use [label]?
326-
return new BreakStatement(null)..fileOffset = fileOffset;
326+
return new BreakStatementImpl(isContinue: false)..fileOffset = fileOffset;
327327
}
328328

329329
/// Return a representation of a catch clause.
@@ -355,7 +355,7 @@ class Forest {
355355
Statement createContinueStatement(int fileOffset, Object label) {
356356
assert(fileOffset != null);
357357
// TODO(johnniwinther): Use [label]?
358-
return new BreakStatement(null)..fileOffset = fileOffset;
358+
return new BreakStatementImpl(isContinue: true)..fileOffset = fileOffset;
359359
}
360360

361361
/// Return a representation of a do statement.

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,15 @@ class InferenceVisitor
339339
}
340340

341341
@override
342-
StatementInferenceResult visitBreakStatement(BreakStatement node) {
342+
StatementInferenceResult visitBreakStatement(
343+
covariant BreakStatementImpl node) {
343344
// TODO(johnniwinther): Refactor break/continue encoding.
344-
inferrer.flowAnalysis.handleBreak(node.target);
345+
assert(node.targetStatement != null);
346+
if (node.isContinue) {
347+
inferrer.flowAnalysis.handleContinue(node.targetStatement);
348+
} else {
349+
inferrer.flowAnalysis.handleBreak(node.targetStatement);
350+
}
345351
return const StatementInferenceResult();
346352
}
347353

@@ -4969,7 +4975,10 @@ class InferenceVisitor
49694975
if (bodyResult.hasChanged) {
49704976
node.body = bodyResult.statement..parent = node;
49714977
}
4972-
inferrer.flowAnalysis.tryFinallyStatement_finallyBegin(node.finalizer);
4978+
// TODO(johnniwinther): Use one internal statement for try-catch-finally.
4979+
TreeNode body = node.body;
4980+
inferrer.flowAnalysis
4981+
.tryFinallyStatement_finallyBegin(body is TryCatch ? body.body : body);
49734982
StatementInferenceResult finalizerResult =
49744983
inferrer.inferStatement(node.finalizer);
49754984
if (finalizerResult.hasChanged) {
@@ -5161,7 +5170,10 @@ class InferenceVisitor
51615170
DartType promotedType;
51625171
DartType declaredOrInferredType = variable.type;
51635172
if (inferrer.isNonNullableByDefault) {
5164-
promotedType = inferrer.flowAnalysis.variableRead(node, variable);
5173+
if (!variable.isLocalFunction) {
5174+
// Don't promote local functions.
5175+
promotedType = inferrer.flowAnalysis.variableRead(node, variable);
5176+
}
51655177
} else {
51665178
bool mutatedInClosure = variable.mutatedInClosure;
51675179
promotedType = inferrer.typePromoter
@@ -5194,6 +5206,7 @@ class InferenceVisitor
51945206

51955207
@override
51965208
StatementInferenceResult visitWhileStatement(WhileStatement node) {
5209+
inferrer.flowAnalysis.whileStatement_conditionBegin(node);
51975210
InterfaceType expectedType =
51985211
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
51995212
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
@@ -5202,10 +5215,12 @@ class InferenceVisitor
52025215
Expression condition =
52035216
inferrer.ensureAssignableResult(expectedType, conditionResult);
52045217
node.condition = condition..parent = node;
5218+
inferrer.flowAnalysis.whileStatement_bodyBegin(node, node.condition);
52055219
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
52065220
if (bodyResult.hasChanged) {
52075221
node.body = bodyResult.statement..parent = node;
52085222
}
5223+
inferrer.flowAnalysis.whileStatement_end();
52095224
return const StatementInferenceResult();
52105225
}
52115226

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import 'dart:core' hide MapEntry;
2222

2323
import 'package:kernel/ast.dart';
24-
24+
import 'package:kernel/text/ast_to_text.dart';
2525
import 'package:kernel/core_types.dart';
2626

2727
import '../fasta_codes.dart'
@@ -195,6 +195,15 @@ class ClassInferenceInfo {
195195
ClassInferenceInfo(this.builder);
196196
}
197197

198+
class BreakStatementImpl extends BreakStatement {
199+
Statement targetStatement;
200+
final bool isContinue;
201+
202+
BreakStatementImpl({this.isContinue})
203+
: assert(isContinue != null),
204+
super(null);
205+
}
206+
198207
enum InternalExpressionKind {
199208
Binary,
200209
Cascade,
@@ -237,8 +246,13 @@ abstract class InternalExpression extends Expression {
237246
InternalExpressionKind get kind;
238247

239248
@override
240-
R accept<R>(ExpressionVisitor<R> visitor) =>
241-
unsupported("${runtimeType}.accept", -1, null);
249+
R accept<R>(ExpressionVisitor<R> visitor) {
250+
if (visitor is Printer || visitor is Precedence) {
251+
// Allow visitors needed for toString.
252+
return visitor.defaultExpression(this);
253+
}
254+
return unsupported("${runtimeType}.accept", -1, null);
255+
}
242256

243257
@override
244258
R accept1<R, A>(ExpressionVisitor1<R, A> visitor, A arg) =>

pkg/front_end/test/id_tests/definite_assignment_test.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,9 @@ main(List<String> args) async {
2525
runTest: runTestFor(
2626
const DefiniteAssignmentDataComputer(), [cfeNonNullableOnlyConfig]),
2727
skipList: [
28-
// TODO(johnniwinther): Update break/continue handling to support these:
29-
'do.dart',
28+
// TODO(johnniwinther): Change for loop encoding to support these:
3029
'for.dart',
3130
'for_each.dart',
32-
'switch.dart',
33-
'while.dart',
3431
]);
3532
}
3633

pkg/front_end/test/id_tests/nullability_test.dart

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ main(List<String> args) async {
1919
createUriForFileName: createUriForFileName,
2020
onFailure: onFailure,
2121
runTest: runTestFor(
22-
const NullabilityDataComputer(), [cfeNonNullableOnlyConfig]),
23-
skipList: [
24-
// TODO(johnniwinther): Run all nullability tests.
25-
'try_finally.dart',
26-
'while.dart',
27-
]);
22+
const NullabilityDataComputer(), [cfeNonNullableOnlyConfig]));
2823
}
2924

3025
class NullabilityDataComputer extends DataComputer<String> {

pkg/front_end/test/id_tests/type_promotion_test.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,8 @@ main(List<String> args) async {
2727
'bug39178.dart',
2828
'constructor_initializer.dart',
2929
'for.dart',
30-
'not_promoted.dart',
3130
'switch.dart',
32-
'try_finally.dart',
3331
'type_parameter.dart',
34-
'while.dart',
3532
'write_capture.dart',
3633
]);
3734
}

0 commit comments

Comments
 (0)