Skip to content

Commit 72bc313

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Unify "why not promoted" representations for for-loops and other writes.
This CL changes the responsibility for doing flow analysis of the implicit variable write in a `for-in` loop as follows: if the `for-in` loop does not declare a variable, but it assigns to a local variable, then the flow analysis client is responsible for calling `write` on entry to the loop. This in turn allows us to use a single code path to track "why not promoted" information related to all possible local variable writes; we no longer need as much special case logic to handle for-in loops. To make the analyzer integration slightly cleaner, we change the argument type of FlowAnalysis.write to Node rather than Expression, so that the analyzer can pass in the ForEachParts object when analyzing a for-in loop. Bug: #44898 Change-Id: I24b47be8eac2e276cd291a5b2f2e4444c911138f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193837 Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 1cffb93 commit 72bc313

File tree

8 files changed

+86
-170
lines changed

8 files changed

+86
-170
lines changed

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart

Lines changed: 36 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,16 @@ class AssignedVariablesNodeInfo<Variable extends Object> {
300300
/// Non-promotion reason describing the situation where a variable was not
301301
/// promoted due to an explicit write to the variable appearing somewhere in the
302302
/// source code.
303-
class DemoteViaExplicitWrite<Variable extends Object, Expression extends Object>
303+
class DemoteViaExplicitWrite<Variable extends Object>
304304
extends NonPromotionReason {
305305
/// The local variable that was not promoted.
306306
final Variable variable;
307307

308-
/// The expression that wrote to the variable; this corresponds to an
309-
/// expression that was passed to [FlowAnalysis.write].
310-
final Expression writeExpression;
308+
/// The node that wrote to the variable; this corresponds to a node that was
309+
/// passed to [FlowAnalysis.write].
310+
final Object node;
311311

312-
DemoteViaExplicitWrite(this.variable, this.writeExpression);
312+
DemoteViaExplicitWrite(this.variable, this.node);
313313

314314
@override
315315
String get documentationLink => 'http://dart.dev/go/non-promo-write';
@@ -318,46 +318,14 @@ class DemoteViaExplicitWrite<Variable extends Object, Expression extends Object>
318318
String get shortName => 'explicitWrite';
319319

320320
@override
321-
R accept<R, Node extends Object, Expression extends Object,
322-
Variable extends Object, Type extends Object>(
323-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
324-
visitor) =>
321+
R accept<R, Node extends Object, Variable extends Object,
322+
Type extends Object>(
323+
NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
325324
visitor.visitDemoteViaExplicitWrite(
326-
this as DemoteViaExplicitWrite<Variable, Expression>);
325+
this as DemoteViaExplicitWrite<Variable>);
327326

328327
@override
329-
String toString() => 'DemoteViaExplicitWrite($writeExpression)';
330-
}
331-
332-
/// Non-promotion reason describing the situation where a variable was not
333-
/// promoted due to the variable appearing before the word `in` in a "for each"
334-
/// statement or a "for each" collection element.
335-
class DemoteViaForEachVariableWrite<Variable extends Object,
336-
Node extends Object> extends NonPromotionReason {
337-
/// The local variable that was not promoted.
338-
final Variable variable;
339-
340-
/// The "for each" statement or collection element that wrote to the variable.
341-
final Node node;
342-
343-
DemoteViaForEachVariableWrite(this.variable, this.node);
344-
345-
@override
346-
String get documentationLink => 'http://dart.dev/go/non-promo-write';
347-
348-
@override
349-
String get shortName => 'explicitWrite';
350-
351-
@override
352-
R accept<R, Node extends Object, Expression extends Object,
353-
Variable extends Object, Type extends Object>(
354-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
355-
visitor) =>
356-
visitor.visitDemoteViaForEachVariableWrite(
357-
this as DemoteViaForEachVariableWrite<Variable, Node>);
358-
359-
@override
360-
String toString() => 'DemoteViaForEachVariableWrite($node)';
328+
String toString() => 'DemoteViaExplicitWrite($node)';
361329
}
362330

363331
/// A collection of flow models representing the possible outcomes of evaluating
@@ -565,11 +533,8 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
565533
/// - Call [forEach_end].
566534
///
567535
/// [node] should be the same node that was passed to
568-
/// [AssignedVariables.endNode] for the for statement. [loopVariable] should
569-
/// be the variable assigned to by the loop (if it is promotable, otherwise
570-
/// null). [writtenType] should be the type written to that variable (i.e.
571-
/// if the loop iterates over `List<Foo>`, it should be `Foo`).
572-
void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType);
536+
/// [AssignedVariables.endNode] for the for statement.
537+
void forEach_bodyBegin(Node node);
573538

574539
/// Call this method just before visiting the body of a "for-in" statement or
575540
/// collection element. See [forEach_bodyBegin] for details.
@@ -982,7 +947,7 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
982947

983948
/// Register write of the given [variable] in the current state.
984949
/// [writtenType] should be the type of the value that was written.
985-
/// [expression] should be the whole expression performing the write.
950+
/// [node] should be the syntactic construct performing the write.
986951
/// [writtenExpression] should be the expression that was written, or `null`
987952
/// if the expression that was written is not directly represented in the
988953
/// source code (this happens, for example, with compound assignments and with
@@ -991,7 +956,7 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
991956
/// This should also be used for the implicit write to a non-final variable in
992957
/// its initializer, to ensure that the type is promoted to non-nullable if
993958
/// necessary; in this case, [viaInitializer] should be `true`.
994-
void write(Expression expression, Variable variable, Type writtenType,
959+
void write(Node node, Variable variable, Type writtenType,
995960
Expression? writtenExpression);
996961

997962
/// Prints out a summary of the current state of flow analysis, intended for
@@ -1162,9 +1127,9 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
11621127
}
11631128

11641129
@override
1165-
void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType) {
1166-
return _wrap('forEach_bodyBegin($node, $loopVariable, $writtenType)',
1167-
() => _wrapped.forEach_bodyBegin(node, loopVariable, writtenType));
1130+
void forEach_bodyBegin(Node node) {
1131+
return _wrap(
1132+
'forEach_bodyBegin($node)', () => _wrapped.forEach_bodyBegin(node));
11681133
}
11691134

11701135
@override
@@ -1513,12 +1478,10 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
15131478
}
15141479

15151480
@override
1516-
void write(Expression expression, Variable variable, Type writtenType,
1481+
void write(Node node, Variable variable, Type writtenType,
15171482
Expression? writtenExpression) {
1518-
_wrap(
1519-
'write($expression, $variable, $writtenType, $writtenExpression)',
1520-
() => _wrapped.write(
1521-
expression, variable, writtenType, writtenExpression));
1483+
_wrap('write($node, $variable, $writtenType, $writtenExpression)',
1484+
() => _wrapped.write(node, variable, writtenType, writtenExpression));
15221485
}
15231486

15241487
@override
@@ -2348,21 +2311,17 @@ abstract class NonPromotionReason {
23482311
String get shortName;
23492312

23502313
/// Implementation of the visitor pattern for non-promotion reasons.
2351-
R accept<R, Node extends Object, Expression extends Object,
2352-
Variable extends Object, Type extends Object>(
2353-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type> visitor);
2314+
R accept<R, Node extends Object, Variable extends Object,
2315+
Type extends Object>(
2316+
NonPromotionReasonVisitor<R, Node, Variable, Type> visitor);
23542317
}
23552318

23562319
/// Implementation of the visitor pattern for non-promotion reasons.
23572320
abstract class NonPromotionReasonVisitor<R, Node extends Object,
2358-
Expression extends Object, Variable extends Object, Type extends Object> {
2321+
Variable extends Object, Type extends Object> {
23592322
NonPromotionReasonVisitor._() : assert(false, 'Do not extend this class');
23602323

2361-
R visitDemoteViaExplicitWrite(
2362-
DemoteViaExplicitWrite<Variable, Expression> reason);
2363-
2364-
R visitDemoteViaForEachVariableWrite(
2365-
DemoteViaForEachVariableWrite<Variable, Node> reason);
2324+
R visitDemoteViaExplicitWrite(DemoteViaExplicitWrite<Variable> reason);
23662325

23672326
R visitPropertyNotPromoted(PropertyNotPromoted<Type> reason);
23682327

@@ -2389,10 +2348,9 @@ class PropertyNotPromoted<Type extends Object> extends NonPromotionReason {
23892348
String get shortName => 'propertyNotPromoted';
23902349

23912350
@override
2392-
R accept<R, Node extends Object, Expression extends Object,
2393-
Variable extends Object, Type extends Object>(
2394-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
2395-
visitor) =>
2351+
R accept<R, Node extends Object, Variable extends Object,
2352+
Type extends Object>(
2353+
NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
23962354
visitor.visitPropertyNotPromoted(this as PropertyNotPromoted<Type>);
23972355
}
23982356

@@ -2627,10 +2585,9 @@ class ThisNotPromoted extends NonPromotionReason {
26272585
String get shortName => 'thisNotPromoted';
26282586

26292587
@override
2630-
R accept<R, Node extends Object, Expression extends Object,
2631-
Variable extends Object, Type extends Object>(
2632-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
2633-
visitor) =>
2588+
R accept<R, Node extends Object, Variable extends Object,
2589+
Type extends Object>(
2590+
NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
26342591
visitor.visitThisNotPromoted(this);
26352592
}
26362593

@@ -3680,22 +3637,14 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
36803637
}
36813638

36823639
@override
3683-
void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType) {
3640+
void forEach_bodyBegin(Node node) {
36843641
AssignedVariablesNodeInfo<Variable> info =
36853642
_assignedVariables._getInfoForNode(node);
36863643
_current = _current.conservativeJoin(info._written, info._captured).split();
36873644
_SimpleStatementContext<Variable, Type> context =
36883645
new _SimpleStatementContext<Variable, Type>(
36893646
_current.reachable.parent!, _current);
36903647
_stack.add(context);
3691-
if (loopVariable != null) {
3692-
_current = _current.write(
3693-
new DemoteViaForEachVariableWrite<Variable, Node>(loopVariable, node),
3694-
loopVariable,
3695-
writtenType,
3696-
new SsaNode<Variable, Type>(null),
3697-
typeOperations);
3698-
}
36993648
}
37003649

37013650
@override
@@ -4220,15 +4169,15 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
42204169
}
42214170

42224171
@override
4223-
void write(Expression expression, Variable variable, Type writtenType,
4172+
void write(Node node, Variable variable, Type writtenType,
42244173
Expression? writtenExpression) {
42254174
ExpressionInfo<Variable, Type>? expressionInfo = writtenExpression == null
42264175
? null
42274176
: _getExpressionInfo(writtenExpression);
42284177
SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
42294178
expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
42304179
_current = _current.write(
4231-
new DemoteViaExplicitWrite<Variable, Expression>(variable, expression),
4180+
new DemoteViaExplicitWrite<Variable>(variable, node),
42324181
variable,
42334182
writtenType,
42344183
newSsaNode,
@@ -4509,8 +4458,7 @@ class _LegacyTypePromotion<Node extends Object, Statement extends Node,
45094458
void for_updaterBegin() {}
45104459

45114460
@override
4512-
void forEach_bodyBegin(
4513-
Node node, Variable? loopVariable, Type? writtenType) {}
4461+
void forEach_bodyBegin(Node node) {}
45144462

45154463
@override
45164464
void forEach_end() {}
@@ -4815,7 +4763,7 @@ class _LegacyTypePromotion<Node extends Object, Statement extends Node,
48154763
}
48164764

48174765
@override
4818-
void write(Expression expression, Variable variable, Type writtenType,
4766+
void write(Node node, Variable variable, Type writtenType,
48194767
Expression? writtenExpression) {
48204768
assert(
48214769
_assignedVariables._anywhere._written.contains(variable),

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5500,8 +5500,8 @@ main() {
55005500
x.expr.whyNotPromoted((reasons) {
55015501
expect(reasons.keys, unorderedEquals([Type('int')]));
55025502
var nonPromotionReason =
5503-
reasons.values.single as DemoteViaExplicitWrite<Var, Expression>;
5504-
expect(nonPromotionReason.writeExpression, same(writeExpression));
5503+
reasons.values.single as DemoteViaExplicitWrite<Var>;
5504+
expect(nonPromotionReason.node, same(writeExpression));
55055505
}).stmt,
55065506
]);
55075507
});
@@ -5523,13 +5523,9 @@ main() {
55235523
checkNotPromoted(x),
55245524
x.expr.whyNotPromoted((reasons) {
55255525
expect(reasons.keys, unorderedEquals([Type('int'), Type('int?')]));
5526-
expect(
5527-
(reasons[Type('int')] as DemoteViaExplicitWrite<Var, Expression>)
5528-
.writeExpression,
5526+
expect((reasons[Type('int')] as DemoteViaExplicitWrite<Var>).node,
55295527
same(writeExpression));
5530-
expect(
5531-
(reasons[Type('int?')] as DemoteViaExplicitWrite<Var, Expression>)
5532-
.writeExpression,
5528+
expect((reasons[Type('int?')] as DemoteViaExplicitWrite<Var>).node,
55335529
same(writeExpression));
55345530
}).stmt,
55355531
]);
@@ -5553,8 +5549,8 @@ main() {
55535549
x.expr.whyNotPromoted((reasons) {
55545550
expect(reasons.keys, unorderedEquals([Type('int')]));
55555551
var nonPromotionReason =
5556-
reasons.values.single as DemoteViaExplicitWrite<Var, Expression>;
5557-
expect(nonPromotionReason.writeExpression, same(writeExpression));
5552+
reasons.values.single as DemoteViaExplicitWrite<Var>;
5553+
expect(nonPromotionReason.node, same(writeExpression));
55585554
}).stmt,
55595555
]);
55605556
});
@@ -5577,8 +5573,8 @@ main() {
55775573
checkPromoted(x, 'num'),
55785574
x.expr.whyNotPromoted((reasons) {
55795575
var nonPromotionReason =
5580-
reasons[Type('int')] as DemoteViaExplicitWrite;
5581-
expect(nonPromotionReason.writeExpression, same(writeExpression));
5576+
reasons[Type('int')] as DemoteViaExplicitWrite<Var>;
5577+
expect(nonPromotionReason.node, same(writeExpression));
55825578
}).stmt,
55835579
]);
55845580
});
@@ -5759,10 +5755,9 @@ class _MockNonPromotionReason extends NonPromotionReason {
57595755

57605756
String get shortName => fail('Unexpected call to shortName');
57615757

5762-
R accept<R, Node extends Object, Expression extends Object,
5763-
Variable extends Object, Type extends Object>(
5764-
NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
5765-
visitor) =>
5758+
R accept<R, Node extends Object, Variable extends Object,
5759+
Type extends Object>(
5760+
NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
57665761
fail('Unexpected call to accept');
57675762
}
57685763

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,11 @@ class _ForEach extends Statement {
11431143
void _visit(
11441144
Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
11451145
var iteratedType = h._getIteratedType(iterable._visit(h, flow));
1146-
flow.forEach_bodyBegin(this, variable, iteratedType);
1146+
flow.forEach_bodyBegin(this);
1147+
var variable = this.variable;
1148+
if (variable != null && !declaresVariable) {
1149+
flow.write(this, variable, iteratedType, null);
1150+
}
11471151
body._visit(h, flow);
11481152
flow.forEach_end();
11491153
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,12 @@ class ForResolver {
132132
?.declare(loopVariable.declaredElement!, true);
133133
}
134134

135-
_resolver.flowAnalysis?.flow?.forEach_bodyBegin(
136-
node,
137-
identifierElement is PromotableElement
138-
? identifierElement
139-
: loopVariable?.declaredElement,
140-
elementType ?? DynamicTypeImpl.instance,
141-
);
135+
_resolver.flowAnalysis?.flow?.forEach_bodyBegin(node);
136+
if (identifierElement is PromotableElement &&
137+
forEachParts is ForEachPartsWithIdentifier) {
138+
_resolver.flowAnalysis?.flow?.write(forEachParts, identifierElement,
139+
elementType ?? DynamicTypeImpl.instance, null);
140+
}
142141

143142
_resolveBody(body);
144143

0 commit comments

Comments
 (0)