Flow analysis: add the ability to defer storing node info in AssignedVariables.
This is needed by the front end, since it sometimes finishes parsing a
construct before creating the kernel representation of it, so it needs
to defer storing the node info for that construct until it creates the
kernel representation.
Change-Id: I2a59f08a228b6fed2af711cc0f06576ddc99a054
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123505
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart b/pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart
index 974dea0..79d5e07 100644
--- a/pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart
+++ b/pkg/front_end/lib/src/fasta/flow_analysis/flow_analysis.dart
@@ -25,42 +25,24 @@
/// statement, try statement, loop collection element, local function, or
/// closure.
class AssignedVariables<Node, Variable> {
- /// Mapping from a node to the set of local variables that are potentially
- /// written to within that node.
- final Map<Node, Set<Variable>> _writtenInNode = {};
+ /// Mapping from a node to the info for that node.
+ final Map<Node, AssignedVariablesNodeInfo<Variable>> _info =
+ new Map<Node, AssignedVariablesNodeInfo<Variable>>.identity();
- /// Mapping from a node to the set of local variables for which a potential
- /// write is captured by a local function or closure inside that node.
- final Map<Node, Set<Variable>> _capturedInNode = {};
+ /// Info for the variables written or captured anywhere in the code being
+ /// analyzed.
+ final AssignedVariablesNodeInfo<Variable> _anywhere =
+ new AssignedVariablesNodeInfo<Variable>();
- /// Set of local variables that are potentially written to anywhere in the
- /// code being analyzed.
- final Set<Variable> _writtenAnywhere = {};
+ /// Stack of info for nodes that have been entered but not yet left.
+ final List<AssignedVariablesNodeInfo<Variable>> _stack = [
+ new AssignedVariablesNodeInfo<Variable>()
+ ];
- /// Set of local variables for which a potential write is captured by a local
- /// function or closure anywhere in the code being analyzed.
- final Set<Variable> _capturedAnywhere = {};
-
- /// Stack of sets accumulating variables that are potentially written to.
- ///
- /// A set is pushed onto the stack when a node is entered, and popped when
- /// a node is left.
- final List<Set<Variable>> _writtenStack = [new Set<Variable>.identity()];
-
- /// Stack of sets accumulating variables that are declared.
- ///
- /// A set is pushed onto the stack when a node is entered, and popped when
- /// a node is left.
- final List<Set<Variable>> _declaredStack = [new Set<Variable>.identity()];
-
- /// Stack of sets accumulating variables for which a potential write is
- /// captured by a local function or closure.
- ///
- /// A set is pushed onto the stack when a node is entered, and popped when
- /// a node is left.
- final List<Set<Variable>> _capturedStack = [new Set<Variable>.identity()];
-
- AssignedVariables();
+ /// When assertions are enabled, the set of info objects that have been
+ /// retrieved by [deferNode] but not yet sent to [storeNode].
+ final Set<AssignedVariablesNodeInfo<Variable>> _deferredInfos =
+ new Set<AssignedVariablesNodeInfo<Variable>>.identity();
/// This method should be called during pre-traversal, to mark the start of a
/// loop statement, switch statement, try statement, loop collection element,
@@ -75,9 +57,7 @@
/// statement, the body of the switch statement should be covered, but the
/// switch expression should not.
void beginNode() {
- _writtenStack.add(new Set<Variable>.identity());
- _declaredStack.add(new Set<Variable>.identity());
- _capturedStack.add(new Set<Variable>.identity());
+ _stack.add(new AssignedVariablesNodeInfo<Variable>());
}
/// This method should be called during pre-traversal, to indicate that the
@@ -86,7 +66,34 @@
/// It is not required for the declaration to be seen prior to its use (this
/// is to allow for error recovery in the analyzer).
void declare(Variable variable) {
- _declaredStack.last.add(variable);
+ _stack.last._declared.add(variable);
+ }
+
+ /// This method may be called during pre-traversal, to mark the end of a
+ /// loop statement, switch statement, try statement, loop collection element,
+ /// local function, or closure which might need to be queried later.
+ ///
+ /// [isClosure] should be true if the node is a local function or closure.
+ ///
+ /// In contrast to [endNode], this method doesn't store the data gathered for
+ /// the node for later use; instead it returns it to the caller. At a later
+ /// time, the caller should pass the returned data to [storeNodeInfo].
+ ///
+ /// See [beginNode] for more details.
+ AssignedVariablesNodeInfo<Variable> deferNode({bool isClosure: false}) {
+ AssignedVariablesNodeInfo<Variable> info = _stack.removeLast();
+ info._written.removeAll(info._declared);
+ info._captured.removeAll(info._declared);
+ AssignedVariablesNodeInfo<Variable> last = _stack.last;
+ last._written.addAll(info._written);
+ last._captured.addAll(info._captured);
+ if (isClosure) {
+ last._captured.addAll(info._written);
+ _anywhere._captured.addAll(info._written);
+ }
+ // If we have already deferred this info, something has gone horribly wrong.
+ assert(_deferredInfos.add(info));
+ return info;
}
/// This method may be called during pre-traversal, to discard the effects of
@@ -101,12 +108,11 @@
/// needed, use [discardNode] to discard the effects of one of the [beginNode]
/// calls.
void discardNode() {
- Set<Variable> declared = _declaredStack.removeLast();
- _declaredStack.last.addAll(declared);
- Set<Variable> written = _writtenStack.removeLast();
- _writtenStack.last.addAll(written);
- Set<Variable> captured = _capturedStack.removeLast();
- _capturedStack.last.addAll(captured);
+ AssignedVariablesNodeInfo<Variable> discarded = _stack.removeLast();
+ AssignedVariablesNodeInfo<Variable> last = _stack.last;
+ last._declared.addAll(discarded._declared);
+ last._written.addAll(discarded._written);
+ last._captured.addAll(discarded._captured);
}
/// This method should be called during pre-traversal, to mark the end of a
@@ -115,47 +121,40 @@
///
/// [isClosure] should be true if the node is a local function or closure.
///
+ /// This is equivalent to a call to [deferNode] followed immediately by a call
+ /// to [storeInfo].
+ ///
/// See [beginNode] for more details.
void endNode(Node node, {bool isClosure: false}) {
- Set<Variable> declaredInThisNode = _declaredStack.removeLast();
- Set<Variable> writtenInThisNode = _writtenStack.removeLast()
- ..removeAll(declaredInThisNode);
- Set<Variable> capturedInThisNode = _capturedStack.removeLast()
- ..removeAll(declaredInThisNode);
- _writtenInNode[node] = writtenInThisNode;
- _capturedInNode[node] = capturedInThisNode;
- _writtenStack.last.addAll(writtenInThisNode);
- _capturedStack.last.addAll(capturedInThisNode);
- if (isClosure) {
- _capturedStack.last.addAll(writtenInThisNode);
- _capturedAnywhere.addAll(writtenInThisNode);
- }
+ storeInfo(node, deferNode(isClosure: isClosure));
}
/// Call this after visiting the code to be analyzed, to check invariants.
void finish() {
assert(() {
- assert(_writtenStack.length == 1,
- "Unexpected written stack: $_writtenStack");
- assert(_declaredStack.length == 1,
- "Unexpected declared stack: $_declaredStack");
- assert(_capturedStack.length == 1,
- "Unexpected captured stack: $_capturedStack");
- Set<Variable> writtenInThisNode = _writtenStack.last;
- Set<Variable> declaredInThisNode = _declaredStack.last;
- Set<Variable> capturedInThisNode = _capturedStack.last;
- Set<Variable> undeclaredWrites =
- writtenInThisNode.difference(declaredInThisNode);
+ assert(
+ _deferredInfos.isEmpty, "Deferred infos not stored: $_deferredInfos");
+ assert(_stack.length == 1, "Unexpected stack: $_stack");
+ AssignedVariablesNodeInfo<Variable> last = _stack.last;
+ Set<Variable> undeclaredWrites = last._written.difference(last._declared);
assert(undeclaredWrites.isEmpty,
'Variables written to but not declared: $undeclaredWrites');
Set<Variable> undeclaredCaptures =
- capturedInThisNode.difference(declaredInThisNode);
+ last._captured.difference(last._declared);
assert(undeclaredCaptures.isEmpty,
'Variables captured but not declared: $undeclaredCaptures');
return true;
}());
}
+ /// This method may be called at any time between a call to [deferNode] and
+ /// the call to [finish], to store assigned variable info for the node.
+ void storeInfo(Node node, AssignedVariablesNodeInfo<Variable> info) {
+ // Caller should not try to store the same piece of info more than once.
+ assert(_deferredInfos.remove(info));
+ _info[node] = info;
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('AssignedVariables(');
@@ -167,31 +166,19 @@
/// This method should be called during pre-traversal, to mark a write to a
/// variable.
void write(Variable variable) {
- _writtenStack.last.add(variable);
- _writtenAnywhere.add(variable);
+ _stack.last._written.add(variable);
+ _anywhere._written.add(variable);
}
- /// Queries the set of variables for which a potential write is captured by a
- /// local function or closure inside the [node].
- Set<Variable> _getCapturedInNode(Node node) {
- return _capturedInNode[node] ??
- (throw new StateError('No information for $node'));
- }
-
- /// Queries the set of variables that are potentially written to inside the
- /// [node].
- Set<Variable> _getWrittenInNode(Node node) {
- return _writtenInNode[node] ??
- (throw new StateError('No information for $node'));
+ /// Queries the information stored for the given [node].
+ AssignedVariablesNodeInfo<Variable> _getInfoForNode(Node node) {
+ return _info[node] ?? (throw new StateError('No information for $node'));
}
void _printOn(StringBuffer sb) {
- sb.write('_writtenInNode=$_writtenInNode,');
- sb.write('_capturedInNode=$_capturedInNode,');
- sb.write('_writtenAnywhere=$_writtenAnywhere,');
- sb.write('_capturedAnywhere=$_capturedAnywhere,');
- sb.write('_writtenStack=$_writtenStack,');
- sb.write('_capturedStack=$_capturedStack');
+ sb.write('_info=$_info,');
+ sb.write('_stack=$_stack,');
+ sb.write('_anywhere=$_anywhere');
}
}
@@ -200,27 +187,17 @@
/// Not intended to be used by clients of flow analysis.
class AssignedVariablesForTesting<Node, Variable>
extends AssignedVariables<Node, Variable> {
- final Map<Node, Set<Variable>> _declaredInNode = {};
+ Set<Variable> get capturedAnywhere => _anywhere._captured;
- Set<Variable> get capturedAnywhere => _capturedAnywhere;
+ Set<Variable> get declaredAtTopLevel => _stack.first._declared;
- Set<Variable> get declaredAtTopLevel => _declaredStack.first;
+ Set<Variable> get writtenAnywhere => _anywhere._written;
- Set<Variable> get writtenAnywhere => _writtenAnywhere;
+ Set<Variable> capturedInNode(Node node) => _getInfoForNode(node)._captured;
- Set<Variable> capturedInNode(Node node) => _getCapturedInNode(node);
+ Set<Variable> declaredInNode(Node node) => _getInfoForNode(node)._declared;
- Set<Variable> declaredInNode(Node node) =>
- _declaredInNode[node] ??
- (throw new StateError('No information for $node'));
-
- @override
- void endNode(Node node, {bool isClosure: false}) {
- _declaredInNode[node] = _declaredStack.last;
- super.endNode(node, isClosure: isClosure);
- }
-
- bool isTracked(Node node) => _writtenInNode.containsKey(node);
+ bool isTracked(Node node) => _info.containsKey(node);
String toString() {
StringBuffer sb = new StringBuffer();
@@ -230,13 +207,24 @@
return sb.toString();
}
- Set<Variable> writtenInNode(Node node) => _getWrittenInNode(node);
+ Set<Variable> writtenInNode(Node node) => _getInfoForNode(node)._written;
+}
- @override
- void _printOn(StringBuffer sb) {
- super._printOn(sb);
- sb.write(',_declaredInNode=$_declaredInNode');
- }
+/// Information tracked by [AssignedVariables] for a single node.
+class AssignedVariablesNodeInfo<Variable> {
+ final Set<Variable> _written = new Set<Variable>.identity();
+
+ // The set of local variables that are potentially written in the node.
+ final Set<Variable> _captured = new Set<Variable>.identity();
+
+ // The set of local variables for which a potential write is captured by a
+ // local function or closure inside the node.
+ final Set<Variable> _declared = new Set<Variable>.identity();
+
+ // The set of local variables that are declared in the node.
+ String toString() =>
+ 'AssignedVariablesNodeInfo(_written=$_written, _captured=$_captured, '
+ '_declared=$_declared)';
}
/// A collection of flow models representing the possible outcomes of evaluating
@@ -2032,14 +2020,12 @@
@override
void doStatement_bodyBegin(Statement doStatement) {
- Iterable<Variable> loopAssigned =
- _assignedVariables._getWrittenInNode(doStatement);
- Iterable<Variable> loopCaptured =
- _assignedVariables._getCapturedInNode(doStatement);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(doStatement);
_BranchTargetContext<Variable, Type> context =
new _BranchTargetContext<Variable, Type>();
_stack.add(context);
- _current = _current.removePromotedAll(loopAssigned, loopCaptured);
+ _current = _current.removePromotedAll(info._written, info._captured);
_statementToContext[doStatement] = context;
}
@@ -2107,11 +2093,9 @@
@override
void for_conditionBegin(Node node) {
- Iterable<Variable> loopAssigned =
- _assignedVariables._getWrittenInNode(node);
- Iterable<Variable> loopCaptured =
- _assignedVariables._getCapturedInNode(node);
- _current = _current.removePromotedAll(loopAssigned, loopCaptured);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(node);
+ _current = _current.removePromotedAll(info._written, info._captured);
}
@override
@@ -2134,14 +2118,12 @@
@override
void forEach_bodyBegin(Node node, Variable loopVariable, Type writtenType) {
- Iterable<Variable> loopAssigned =
- _assignedVariables._getWrittenInNode(node);
- Iterable<Variable> loopCaptured =
- _assignedVariables._getCapturedInNode(node);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(node);
_SimpleStatementContext<Variable, Type> context =
new _SimpleStatementContext<Variable, Type>(_current);
_stack.add(context);
- _current = _current.removePromotedAll(loopAssigned, loopCaptured);
+ _current = _current.removePromotedAll(info._written, info._captured);
if (loopVariable != null) {
_current = _current.write(loopVariable, writtenType, typeOperations);
}
@@ -2156,13 +2138,13 @@
@override
void functionExpression_begin(Node node) {
- Iterable<Variable> writeCaptured =
- _assignedVariables._getWrittenInNode(node);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(node);
++_functionNestingLevel;
- _current = _current.removePromotedAll(const [], writeCaptured);
+ _current = _current.removePromotedAll(const [], info._written);
_stack.add(new _SimpleContext(_current));
- _current = _current.removePromotedAll(_assignedVariables._writtenAnywhere,
- _assignedVariables._capturedAnywhere);
+ _current = _current.removePromotedAll(_assignedVariables._anywhere._written,
+ _assignedVariables._anywhere._captured);
}
@override
@@ -2363,12 +2345,13 @@
@override
void switchStatement_beginCase(bool hasLabel, Node node) {
- Iterable<Variable> notPromoted = _assignedVariables._getWrittenInNode(node);
- Iterable<Variable> captured = _assignedVariables._getCapturedInNode(node);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(node);
_SimpleStatementContext<Variable, Type> context =
_stack.last as _SimpleStatementContext<Variable, Type>;
if (hasLabel) {
- _current = context._previous.removePromotedAll(notPromoted, captured);
+ _current =
+ context._previous.removePromotedAll(info._written, info._captured);
} else {
_current = context._previous;
}
@@ -2405,15 +2388,13 @@
@override
void tryCatchStatement_bodyEnd(Node body) {
- Iterable<Variable> assignedInBody =
- _assignedVariables._getWrittenInNode(body);
- Iterable<Variable> capturedInBody =
- _assignedVariables._getCapturedInNode(body);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(body);
_TryContext<Variable, Type> context =
_stack.last as _TryContext<Variable, Type>;
FlowModel<Variable, Type> beforeBody = context._previous;
FlowModel<Variable, Type> beforeCatch =
- beforeBody.removePromotedAll(assignedInBody, capturedInBody);
+ beforeBody.removePromotedAll(info._written, info._captured);
context._beforeCatch = beforeCatch;
context._afterBodyAndCatches = _current;
}
@@ -2454,25 +2435,23 @@
@override
void tryFinallyStatement_end(Node finallyBlock) {
- Iterable<Variable> assignedInFinally =
- _assignedVariables._getWrittenInNode(finallyBlock);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(finallyBlock);
_TryContext<Variable, Type> context =
_stack.removeLast() as _TryContext<Variable, Type>;
_current = _current.restrict(
- typeOperations, context._afterBodyAndCatches, assignedInFinally);
+ typeOperations, context._afterBodyAndCatches, info._written);
}
@override
void tryFinallyStatement_finallyBegin(Node body) {
- Iterable<Variable> assignedInBody =
- _assignedVariables._getWrittenInNode(body);
- Iterable<Variable> capturedInBody =
- _assignedVariables._getCapturedInNode(body);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(body);
_TryContext<Variable, Type> context =
_stack.last as _TryContext<Variable, Type>;
context._afterBodyAndCatches = _current;
_current = _join(_current,
- context._previous.removePromotedAll(assignedInBody, capturedInBody));
+ context._previous.removePromotedAll(info._written, info._captured));
}
@override
@@ -2494,11 +2473,9 @@
@override
void whileStatement_conditionBegin(Node node) {
- Iterable<Variable> loopAssigned =
- _assignedVariables._getWrittenInNode(node);
- Iterable<Variable> loopCaptured =
- _assignedVariables._getCapturedInNode(node);
- _current = _current.removePromotedAll(loopAssigned, loopCaptured);
+ AssignedVariablesNodeInfo<Variable> info =
+ _assignedVariables._getInfoForNode(node);
+ _current = _current.removePromotedAll(info._written, info._captured);
}
@override
@@ -2511,7 +2488,7 @@
@override
void write(Variable variable, Type writtenType) {
assert(
- _assignedVariables._writtenAnywhere.contains(variable),
+ _assignedVariables._anywhere._written.contains(variable),
"Variable is written to, but was not included in "
"_variablesWrittenAnywhere: $variable");
_current = _current.write(variable, writtenType, typeOperations);
diff --git a/pkg/front_end/test/fasta/flow_analysis/assigned_variables_test.dart b/pkg/front_end/test/fasta/flow_analysis/assigned_variables_test.dart
index 2a6ed67..6537f21 100644
--- a/pkg/front_end/test/fasta/flow_analysis/assigned_variables_test.dart
+++ b/pkg/front_end/test/fasta/flow_analysis/assigned_variables_test.dart
@@ -239,6 +239,33 @@
assignedVariables.finish();
expect(assignedVariables.capturedInNode(node), unorderedEquals([v1, v2]));
});
+
+ test('deferNode allows deferring of node info', () {
+ var assignedVariables = AssignedVariablesForTesting<_Node, _Variable>();
+ var v1 = _Variable('v1');
+ var v2 = _Variable('v2');
+ var v3 = _Variable('v3');
+ var v4 = _Variable('v4');
+ assignedVariables.declare(v1);
+ assignedVariables.declare(v2);
+ assignedVariables.declare(v4);
+ assignedVariables.beginNode();
+ assignedVariables.write(v1);
+ assignedVariables.declare(v3);
+ assignedVariables.beginNode();
+ assignedVariables.write(v2);
+ assignedVariables.endNode(_Node(), isClosure: true);
+ var info = assignedVariables.deferNode();
+ assignedVariables.beginNode();
+ assignedVariables.write(v4);
+ assignedVariables.endNode(_Node());
+ var node = _Node();
+ assignedVariables.storeInfo(node, info);
+ assignedVariables.finish();
+ expect(assignedVariables.declaredInNode(node), [v3]);
+ expect(assignedVariables.writtenInNode(node), unorderedEquals([v1, v2]));
+ expect(assignedVariables.capturedInNode(node), [v2]);
+ });
}
class _Node {}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 5e47e20..91a8629 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1212,6 +1212,7 @@
goes
going
good
+gone
got
gotten
governed
@@ -1288,6 +1289,7 @@
holds
hood
hooks
+horribly
host
hostnames
how
@@ -2025,6 +2027,7 @@
physical
pick
picked
+piece
pipeline
piping
pivot