Rename FlowModel.removePromotedAll to .conservativeJoin.

This makes the implementation consistent with the spec text in
1ed6bab4cc.

Change-Id: Ie25b928c5aacb45a18e85c6f9196b5b55f083478
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152983
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2020-07-01 13:46:48 +00:00 committed by commit-bot@chromium.org
parent 6d3928554c
commit 63feed82e1
2 changed files with 73 additions and 72 deletions

View file

@ -1188,6 +1188,63 @@ class FlowModel<Variable, Type> {
}());
}
/// Updates the state to indicate that the given [writtenVariables] are no
/// longer promoted and are no longer definitely unassigned, and the given
/// [capturedVariables] have been captured by closures.
///
/// This is used at the top of loops to conservatively cancel the promotion of
/// variables that are modified within the loop, so that we correctly analyze
/// code like the following:
///
/// if (x is int) {
/// x.isEven; // OK, promoted to int
/// while (true) {
/// x.isEven; // ERROR: promotion lost
/// x = 'foo';
/// }
/// }
///
/// Note that a more accurate analysis would be to iterate to a fixed point,
/// and only remove promotions if it can be shown that they aren't restored
/// later in the loop body. If we switch to a fixed point analysis, we should
/// be able to remove this method.
FlowModel<Variable, Type> conservativeJoin(
Iterable<Variable> writtenVariables,
Iterable<Variable> capturedVariables) {
Map<Variable, VariableModel<Variable, Type>> newVariableInfo;
for (Variable variable in writtenVariables) {
VariableModel<Variable, Type> info = infoFor(variable);
VariableModel<Variable, Type> newInfo =
info.discardPromotionsAndMarkNotUnassigned();
if (!identical(info, newInfo)) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = newInfo;
}
}
for (Variable variable in capturedVariables) {
VariableModel<Variable, Type> info = variableInfo[variable];
if (info == null) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = new VariableModel<Variable, Type>(
null, const [], false, false, true);
} else if (!info.writeCaptured) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = info.writeCapture();
}
}
FlowModel<Variable, Type> result = newVariableInfo == null
? this
: new FlowModel<Variable, Type>._(reachable, newVariableInfo);
return result;
}
/// Register a declaration of the [variable].
/// Should also be called for function parameters.
///
@ -1236,62 +1293,6 @@ class FlowModel<Variable, Type> {
return new FlowModel<Variable, Type>._(reachable, newVariableInfo);
}
/// Updates the state to indicate that the given [writtenVariables] are no
/// longer promoted; they are presumed to have their declared types.
///
/// This is used at the top of loops to conservatively cancel the promotion of
/// variables that are modified within the loop, so that we correctly analyze
/// code like the following:
///
/// if (x is int) {
/// x.isEven; // OK, promoted to int
/// while (true) {
/// x.isEven; // ERROR: promotion lost
/// x = 'foo';
/// }
/// }
///
/// Note that a more accurate analysis would be to iterate to a fixed point,
/// and only remove promotions if it can be shown that they aren't restored
/// later in the loop body. If we switch to a fixed point analysis, we should
/// be able to remove this method.
FlowModel<Variable, Type> removePromotedAll(
Iterable<Variable> writtenVariables,
Iterable<Variable> capturedVariables) {
Map<Variable, VariableModel<Variable, Type>> newVariableInfo;
for (Variable variable in writtenVariables) {
VariableModel<Variable, Type> info = infoFor(variable);
VariableModel<Variable, Type> newInfo =
info.discardPromotionsAndMarkNotUnassigned();
if (!identical(info, newInfo)) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = newInfo;
}
}
for (Variable variable in capturedVariables) {
VariableModel<Variable, Type> info = variableInfo[variable];
if (info == null) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = new VariableModel<Variable, Type>(
null, const [], false, false, true);
} else if (!info.writeCaptured) {
(newVariableInfo ??=
new Map<Variable, VariableModel<Variable, Type>>.from(
variableInfo))[variable] = info.writeCapture();
}
}
FlowModel<Variable, Type> result = newVariableInfo == null
? this
: new FlowModel<Variable, Type>._(reachable, newVariableInfo);
return result;
}
/// Updates the state to reflect a control path that is known to have
/// previously passed through some [other] state.
///
@ -2383,7 +2384,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
_BranchTargetContext<Variable, Type> context =
new _BranchTargetContext<Variable, Type>();
_stack.add(context);
_current = _current.removePromotedAll(info._written, info._captured);
_current = _current.conservativeJoin(info._written, info._captured);
_statementToContext[doStatement] = context;
}
@ -2453,7 +2454,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
void for_conditionBegin(Node node) {
AssignedVariablesNodeInfo<Variable> info =
_assignedVariables._getInfoForNode(node);
_current = _current.removePromotedAll(info._written, info._captured);
_current = _current.conservativeJoin(info._written, info._captured);
}
@override
@ -2481,7 +2482,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
_SimpleStatementContext<Variable, Type> context =
new _SimpleStatementContext<Variable, Type>(_current);
_stack.add(context);
_current = _current.removePromotedAll(info._written, info._captured);
_current = _current.conservativeJoin(info._written, info._captured);
if (loopVariable != null) {
_current = _current.write(loopVariable, writtenType, typeOperations);
}
@ -2499,9 +2500,9 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
AssignedVariablesNodeInfo<Variable> info =
_assignedVariables._getInfoForNode(node);
++_functionNestingLevel;
_current = _current.removePromotedAll(const [], info._written);
_current = _current.conservativeJoin(const [], info._written);
_stack.add(new _SimpleContext(_current));
_current = _current.removePromotedAll(_assignedVariables._anywhere._written,
_current = _current.conservativeJoin(_assignedVariables._anywhere._written,
_assignedVariables._anywhere._captured);
}
@ -2726,7 +2727,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
_stack.last as _SimpleStatementContext<Variable, Type>;
if (hasLabel) {
_current =
context._previous.removePromotedAll(info._written, info._captured);
context._previous.conservativeJoin(info._written, info._captured);
} else {
_current = context._previous;
}
@ -2772,7 +2773,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
AssignedVariablesNodeInfo<Variable> info =
_assignedVariables._getInfoForNode(body);
FlowModel<Variable, Type> beforeCatch =
beforeBody.removePromotedAll(info._written, info._captured);
beforeBody.conservativeJoin(info._written, info._captured);
context._beforeCatch = beforeCatch;
context._afterBodyAndCatches = afterBody;
@ -2830,7 +2831,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
_stack.last as _TryContext<Variable, Type>;
context._afterBodyAndCatches = _current;
_current = _join(_current,
context._previous.removePromotedAll(info._written, info._captured));
context._previous.conservativeJoin(info._written, info._captured));
}
@override
@ -2854,7 +2855,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
void whileStatement_conditionBegin(Node node) {
AssignedVariablesNodeInfo<Variable> info =
_assignedVariables._getInfoForNode(node);
_current = _current.removePromotedAll(info._written, info._captured);
_current = _current.conservativeJoin(info._written, info._captured);
}
@override

View file

@ -2000,7 +2000,7 @@ main() {
x: _matchVariableModel(chain: null),
});
var s2 = s1.removePromotedAll([], [x]);
var s2 = s1.conservativeJoin([], [x]);
expect(s2.variableInfo, {
x: _matchVariableModel(chain: null, writeCaptured: true),
});
@ -2432,14 +2432,14 @@ main() {
});
});
group('removePromotedAll', () {
group('conservativeJoin', () {
test('unchanged', () {
var h = _Harness();
var s1 = FlowModel<_Var, _Type>(true)
.declare(intQVar, true)
.tryPromoteForTypeCheck(h, objectQVar, _Type('int'))
.ifTrue;
var s2 = s1.removePromotedAll([intQVar], []);
var s2 = s1.conservativeJoin([intQVar], []);
expect(s2, same(s1));
});
@ -2450,7 +2450,7 @@ main() {
.ifTrue
.tryPromoteForTypeCheck(h, intQVar, _Type('int'))
.ifTrue;
var s2 = s1.removePromotedAll([intQVar], []);
var s2 = s1.conservativeJoin([intQVar], []);
expect(s2.reachable, true);
expect(s2.variableInfo, {
objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
@ -2465,7 +2465,7 @@ main() {
.ifTrue
.tryPromoteForTypeCheck(h, intQVar, _Type('int'))
.ifTrue;
var s2 = s1.removePromotedAll([], [intQVar]);
var s2 = s1.conservativeJoin([], [intQVar]);
expect(s2.reachable, true);
expect(s2.variableInfo, {
objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
@ -2518,8 +2518,8 @@ main() {
.declare(c, false)
.declare(d, false);
// In s1, a and b are write captured. In s2, a and c are.
var s1 = s0.removePromotedAll([a, b], [a, b]);
var s2 = s1.removePromotedAll([a, c], [a, c]);
var s1 = s0.conservativeJoin([a, b], [a, b]);
var s2 = s1.conservativeJoin([a, c], [a, c]);
var result = s2.restrict(h, s1, Set());
expect(
result.infoFor(a),