Skip to content

Commit 4d97926

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow anaylsis: fix definite assignment tracking for for-each loop vars
Change-Id: Ia0dfd94070e28832c5ffc6fb892626d9036042a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117820 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent b43bb45 commit 4d97926

File tree

6 files changed

+83
-23
lines changed

6 files changed

+83
-23
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ class FlowAnalysisHelper {
244244
return false;
245245
}
246246

247-
void loopVariable(DeclaredIdentifier loopVariable) {
248-
if (loopVariable != null) {
249-
flow.add(loopVariable.declaredElement, assigned: true);
247+
void loopVariable(DeclaredIdentifier declaredVariable) {
248+
if (declaredVariable != null) {
249+
flow.add(declaredVariable.declaredElement, assigned: false);
250250
}
251251
}
252252

@@ -427,6 +427,16 @@ class _AssignedVariablesVisitor extends RecursiveAstVisitor<void> {
427427
iterable.accept(this);
428428

429429
assignedVariables.beginNode();
430+
if (forLoopParts is ForEachPartsWithIdentifier) {
431+
var element = forLoopParts.identifier.staticElement;
432+
if (element is VariableElement) {
433+
assignedVariables.write(element);
434+
}
435+
} else if (forLoopParts is ForEachPartsWithDeclaration) {
436+
assignedVariables.write(forLoopParts.loopVariable.declaredElement);
437+
} else {
438+
throw new StateError('Unrecognized for loop parts');
439+
}
430440
body.accept(this);
431441
assignedVariables.endNode(node);
432442
} else {

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3850,18 +3850,19 @@ class ResolverVisitor extends ScopedVisitor {
38503850
Expression iterable = forLoopParts.iterable;
38513851
DeclaredIdentifier loopVariable;
38523852
DartType valueType;
3853+
Element identifierElement;
38533854
if (forLoopParts is ForEachPartsWithDeclaration) {
38543855
loopVariable = forLoopParts.loopVariable;
38553856
valueType = loopVariable?.type?.type ?? UnknownInferredType.instance;
38563857
} else if (forLoopParts is ForEachPartsWithIdentifier) {
38573858
SimpleIdentifier identifier = forLoopParts.identifier;
38583859
identifier?.accept(this);
3859-
Element element = identifier?.staticElement;
3860-
if (element is VariableElement) {
3861-
valueType = element.type;
3862-
} else if (element is PropertyAccessorElement) {
3863-
if (element.parameters.isNotEmpty) {
3864-
valueType = element.parameters[0].type;
3860+
identifierElement = identifier?.staticElement;
3861+
if (identifierElement is VariableElement) {
3862+
valueType = identifierElement.type;
3863+
} else if (identifierElement is PropertyAccessorElement) {
3864+
if (identifierElement.parameters.isNotEmpty) {
3865+
valueType = identifierElement.parameters[0].type;
38653866
}
38663867
}
38673868
}
@@ -3880,7 +3881,10 @@ class ResolverVisitor extends ScopedVisitor {
38803881
_flowAnalysis?.loopVariable(loopVariable);
38813882
loopVariable?.accept(this);
38823883
_flowAnalysis?.flow?.forEach_bodyBegin(
3883-
_flowAnalysis.assignedVariables.writtenInNode(node));
3884+
_flowAnalysis.assignedVariables.writtenInNode(node),
3885+
identifierElement is VariableElement
3886+
? identifierElement
3887+
: loopVariable.declaredElement);
38843888
node.body?.accept(this);
38853889
_flowAnalysis?.flow?.forEach_end();
38863890

@@ -3920,6 +3924,7 @@ class ResolverVisitor extends ScopedVisitor {
39203924
Expression iterable = forLoopParts.iterable;
39213925
DeclaredIdentifier loopVariable;
39223926
SimpleIdentifier identifier;
3927+
Element identifierElement;
39233928
if (forLoopParts is ForEachPartsWithDeclaration) {
39243929
loopVariable = forLoopParts.loopVariable;
39253930
} else if (forLoopParts is ForEachPartsWithIdentifier) {
@@ -3933,12 +3938,12 @@ class ResolverVisitor extends ScopedVisitor {
39333938
valueType = typeAnnotation?.type ?? UnknownInferredType.instance;
39343939
}
39353940
if (identifier != null) {
3936-
Element element = identifier.staticElement;
3937-
if (element is VariableElement) {
3938-
valueType = element.type;
3939-
} else if (element is PropertyAccessorElement) {
3940-
if (element.parameters.isNotEmpty) {
3941-
valueType = element.parameters[0].type;
3941+
identifierElement = identifier.staticElement;
3942+
if (identifierElement is VariableElement) {
3943+
valueType = identifierElement.type;
3944+
} else if (identifierElement is PropertyAccessorElement) {
3945+
if (identifierElement.parameters.isNotEmpty) {
3946+
valueType = identifierElement.parameters[0].type;
39423947
}
39433948
}
39443949
}
@@ -3957,8 +3962,10 @@ class ResolverVisitor extends ScopedVisitor {
39573962
loopVariable?.accept(this);
39583963

39593964
_flowAnalysis?.flow?.forEach_bodyBegin(
3960-
_flowAnalysis.assignedVariables.writtenInNode(node),
3961-
);
3965+
_flowAnalysis.assignedVariables.writtenInNode(node),
3966+
identifierElement is VariableElement
3967+
? identifierElement
3968+
: loopVariable.declaredElement);
39623969

39633970
Statement body = node.body;
39643971
if (body != null) {

pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,10 +407,15 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
407407
/// - Call [forEach_end].
408408
///
409409
/// [loopAssigned] should be the set of variables that are assigned anywhere
410-
/// in the loop's body.
411-
void forEach_bodyBegin(Set<Variable> loopAssigned) {
410+
/// in the loop's body. [loopVariable] should be the loop variable, if it's a
411+
/// local variable, or `null` otherwise.
412+
void forEach_bodyBegin(Set<Variable> loopAssigned, Variable loopVariable) {
412413
_stack.add(_current);
413414
_current = _current.removePromotedAll(loopAssigned, _referencedVariables);
415+
if (loopVariable != null) {
416+
assert(loopAssigned.contains(loopVariable));
417+
_current = _current.write(typeOperations, loopVariable);
418+
}
414419
}
415420

416421
/// Call this method just before visiting the body of a "for-in" statement or

pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,31 @@ main() {
331331
h.declare(x, initialized: true);
332332
h.promote(x, 'int');
333333
expect(flow.promotedType(x).type, 'int');
334-
flow.forEach_bodyBegin({x});
334+
flow.forEach_bodyBegin({x}, null);
335335
expect(flow.promotedType(x), isNull);
336336
flow.forEach_end();
337337
});
338338
});
339339

340+
test('forEach_bodyBegin() writes to loop variable', () {
341+
var h = _Harness();
342+
var x = h.addVar('x', 'int?');
343+
h.run((flow) {
344+
h.declare(x, initialized: false);
345+
expect(flow.isAssigned(x), false);
346+
flow.forEach_bodyBegin({x}, x);
347+
expect(flow.isAssigned(x), true);
348+
flow.forEach_end();
349+
expect(flow.isAssigned(x), false);
350+
});
351+
});
352+
340353
test('forEach_end() restores state before loop', () {
341354
var h = _Harness();
342355
var x = h.addVar('x', 'int?');
343356
h.run((flow) {
344357
h.declare(x, initialized: true);
345-
flow.forEach_bodyBegin({});
358+
flow.forEach_bodyBegin({}, null);
346359
h.promote(x, 'int');
347360
expect(flow.promotedType(x).type, 'int');
348361
flow.forEach_end();

pkg/front_end/test/flow_analysis/definite_assignment/data/for_each.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,33 @@ forEach_continue(bool c) {
3333
/*unassigned*/ v2;
3434
}
3535

36+
forEach_assigns_to_identifier() {
37+
late int i;
38+
for (i in [0, 1, 2]) {
39+
i;
40+
}
41+
/*unassigned*/ i;
42+
}
43+
44+
forEach_assigns_to_declared_var() {
45+
for (int i in [0, 1, 2]) {
46+
i;
47+
}
48+
}
49+
3650
collection_forEach() {
3751
late Object v1, v2;
3852
[for (var _ in (v1 = [0, 1, 2])) (v2 = 0)];
3953
v1;
4054
/*unassigned*/ v2;
4155
}
56+
57+
collection_forEach_assigns_to_identifier() {
58+
late int i;
59+
[for (i in [0, 1, 2]) i];
60+
/*unassigned*/ i;
61+
}
62+
63+
collection_forEach_assigns_to_declared_var() {
64+
[for (int i in [0, 1, 2]) i];
65+
}

pkg/nnbd_migration/lib/src/edge_builder.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1816,7 +1816,8 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
18161816
_flowAnalysis.add(parts.loopVariable.declaredElement, assigned: true);
18171817
}
18181818
_checkExpressionNotNull(parts.iterable);
1819-
_flowAnalysis.forEach_bodyBegin(_assignedVariables.writtenInNode(node));
1819+
_flowAnalysis.forEach_bodyBegin(
1820+
_assignedVariables.writtenInNode(node), null);
18201821
}
18211822

18221823
// The condition may fail/iterable may be empty, so the body gets a new

0 commit comments

Comments
 (0)