Flow analysis: move tracking of definite assignment into VariableModel class.
Change-Id: I28cdc6f65aa3488da3538a7b920dc10f87ec1de0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115003
Reviewed-by: Konstantin Shcheglov <scheglov@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 48999a6..f220caf 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
@@ -136,8 +136,6 @@
return result;
}
- final _VariableSet<Variable> _emptySet;
-
/// The [NodeOperations], used to manipulate expressions.
final NodeOperations<Expression> nodeOperations;
@@ -188,22 +186,10 @@
TypeOperations<Variable, Type> typeOperations,
FunctionBodyAccess<Variable> functionBody,
) {
- _VariableSet<Variable> emptySet =
- new FlowModel<Variable, Type>(false).notAssigned;
- return new FlowAnalysis._(
- nodeOperations,
- typeOperations,
- functionBody,
- emptySet,
- );
+ return new FlowAnalysis._(nodeOperations, typeOperations, functionBody);
}
- FlowAnalysis._(
- this.nodeOperations,
- this.typeOperations,
- this.functionBody,
- this._emptySet,
- ) {
+ FlowAnalysis._(this.nodeOperations, this.typeOperations, this.functionBody) {
_current = new FlowModel<Variable, Type>(true);
}
@@ -520,7 +506,16 @@
/// Return whether the [variable] is definitely assigned in the current state.
bool isAssigned(Variable variable) {
_variableReferenced(variable);
- return !_current.notAssigned.contains(variable);
+ VariableModel<Type> variableInfo = _current.variableInfo[variable];
+ if (variableInfo == null) {
+ // In error-free code, variables should always be registered with flow
+ // analysis before they're used. But this can't be relied on when the
+ // analyzer is doing error recovery. So if we encounter a variable that
+ // hasn't been registered with flow analysis yet, assume it's unassigned.
+ return false;
+ } else {
+ return variableInfo.assigned;
+ }
}
void isExpression_end(
@@ -692,12 +687,7 @@
void tryFinallyStatement_end(Set<Variable> assignedInFinally) {
_variablesReferenced(assignedInFinally);
FlowModel<Variable, Type> afterBody = _stack.removeLast();
- _current = _current.restrict(
- typeOperations,
- _emptySet,
- afterBody,
- assignedInFinally,
- );
+ _current = _current.restrict(typeOperations, afterBody, assignedInFinally);
}
void tryFinallyStatement_finallyBegin(Iterable<Variable> assignedInBody) {
@@ -737,7 +727,7 @@
/// Register write of the given [variable] in the current state.
void write(Variable variable) {
_variableReferenced(variable);
- _current = _current.write(typeOperations, _emptySet, variable);
+ _current = _current.write(typeOperations, variable);
}
void _conditionalEnd(Expression condition) {
@@ -787,10 +777,6 @@
/// Indicates whether this point in the control flow is reachable.
final bool reachable;
- /// The set of variables that are not yet definitely assigned at this point in
- /// the control flow.
- final _VariableSet<Variable> notAssigned;
-
/// For each variable being tracked by flow analysis, the variable's model.
///
/// Flow analysis has no awareness of scope, so variables that are out of
@@ -810,15 +796,10 @@
FlowModel(bool reachable)
: this._(
reachable,
- new _VariableSet<Variable>._(const []),
const {},
);
- FlowModel._(
- this.reachable,
- this.notAssigned,
- this.variableInfo,
- ) {
+ FlowModel._(this.reachable, this.variableInfo) {
assert(() {
for (VariableModel<Type> value in variableInfo.values) {
assert(value != null);
@@ -831,17 +812,11 @@
/// optional [assigned] boolean indicates whether the variable is assigned at
/// the point of declaration.
FlowModel<Variable, Type> add(Variable variable, {bool assigned: false}) {
- _VariableSet<Variable> newNotAssigned =
- assigned ? notAssigned : notAssigned.add(variable);
Map<Variable, VariableModel<Type>> newVariableInfo =
new Map<Variable, VariableModel<Type>>.from(variableInfo);
- newVariableInfo[variable] = new VariableModel<Type>(null);
+ newVariableInfo[variable] = new VariableModel<Type>(null, assigned);
- return new FlowModel<Variable, Type>._(
- reachable,
- newNotAssigned,
- newVariableInfo,
- );
+ return new FlowModel<Variable, Type>._(reachable, newVariableInfo);
}
/// Updates the state to indicate that the given [variable] has been
@@ -914,11 +889,7 @@
if (identical(newVariableInfo, variableInfo)) return this;
- return new FlowModel<Variable, Type>._(
- reachable,
- notAssigned,
- newVariableInfo,
- );
+ return new FlowModel<Variable, Type>._(reachable, newVariableInfo);
}
/// Updates the state to reflect a control path that is known to have
@@ -942,22 +913,10 @@
/// block are considered "unsafe" because the assignment might have cancelled
/// the effect of any promotion that occurred inside the `try` block.
FlowModel<Variable, Type> restrict(
- TypeOperations<Variable, Type> typeOperations,
- _VariableSet<Variable> emptySet,
- FlowModel<Variable, Type> other,
- Set<Variable> unsafe,
- ) {
+ TypeOperations<Variable, Type> typeOperations,
+ FlowModel<Variable, Type> other,
+ Set<Variable> unsafe) {
bool newReachable = reachable && other.reachable;
- _VariableSet<Variable> newNotAssigned = notAssigned.intersect(
- empty: emptySet,
- other: other.notAssigned,
- );
- if (newNotAssigned.variables.length == notAssigned.variables.length) {
- newNotAssigned = notAssigned;
- } else if (newNotAssigned.variables.length ==
- other.notAssigned.variables.length) {
- newNotAssigned = other.notAssigned;
- }
Map<Variable, VariableModel<Type>> newVariableInfo =
<Variable, VariableModel<Type>>{};
@@ -985,13 +944,7 @@
newVariableInfo = other.variableInfo;
}
- return _identicalOrNew(
- this,
- other,
- newReachable,
- newNotAssigned,
- newVariableInfo,
- );
+ return _identicalOrNew(this, other, newReachable, newVariableInfo);
}
/// Updates the state to indicate whether the control flow path is
@@ -999,54 +952,23 @@
FlowModel<Variable, Type> setReachable(bool reachable) {
if (this.reachable == reachable) return this;
- return new FlowModel<Variable, Type>._(
- reachable,
- notAssigned,
- variableInfo,
- );
+ return new FlowModel<Variable, Type>._(reachable, variableInfo);
}
@override
- String toString() => '($reachable, $notAssigned, $variableInfo)';
+ String toString() => '($reachable, $variableInfo)';
/// Updates the state to indicate that an assignment was made to the given
/// [variable]. The variable is marked as definitely assigned, and any
/// previous type promotion is removed.
///
/// TODO(paulberry): allow for writes that preserve type promotions.
- FlowModel<Variable, Type> write(TypeOperations<Variable, Type> typeOperations,
- _VariableSet<Variable> emptySet, Variable variable) {
- _VariableSet<Variable> newNotAssigned =
- typeOperations.isLocalVariable(variable)
- ? notAssigned.remove(emptySet, variable)
- : notAssigned;
-
- Map<Variable, VariableModel<Type>> newVariableInfo =
- _removePromoted(variableInfo, variable);
-
- if (identical(newNotAssigned, notAssigned) &&
- identical(newVariableInfo, variableInfo)) {
- return this;
- }
-
- return new FlowModel<Variable, Type>._(
- reachable,
- newNotAssigned,
- newVariableInfo,
- );
- }
-
- /// Updates a "variableInfo" [map] to indicate that a [variable] is no longer
- /// promoted, treating the map as immutable.
- Map<Variable, VariableModel<Type>> _removePromoted(
- Map<Variable, VariableModel<Type>> map, Variable variable) {
- VariableModel<Type> info = map[variable];
- if (info.promotedType == null) return map;
-
- Map<Variable, VariableModel<Type>> result =
- new Map<Variable, VariableModel<Type>>.from(map);
- result[variable] = info.withPromotedType(null);
- return result;
+ FlowModel<Variable, Type> write(
+ TypeOperations<Variable, Type> typeOperations, Variable variable) {
+ VariableModel<Type> infoForVar = variableInfo[variable];
+ VariableModel<Type> newInfoForVar = infoForVar.write();
+ if (identical(newInfoForVar, infoForVar)) return this;
+ return _updateVariableInfo(variable, newInfoForVar);
}
/// Updates a "variableInfo" [map] to indicate that a set of [variable] is no
@@ -1084,8 +1006,7 @@
Map<Variable, VariableModel<Type>> newVariableInfo =
new Map<Variable, VariableModel<Type>>.from(variableInfo);
newVariableInfo[variable] = model;
- return new FlowModel<Variable, Type>._(
- reachable, notAssigned, newVariableInfo);
+ return new FlowModel<Variable, Type>._(reachable, newVariableInfo);
}
/// Forms a new state to reflect a control flow path that might have come from
@@ -1109,19 +1030,12 @@
if (!first.reachable && second.reachable) return second;
bool newReachable = first.reachable || second.reachable;
- _VariableSet<Variable> newNotAssigned =
- first.notAssigned.union(second.notAssigned);
Map<Variable, VariableModel<Type>> newVariableInfo =
FlowModel.joinVariableInfo(
typeOperations, first.variableInfo, second.variableInfo);
return FlowModel._identicalOrNew(
- first,
- second,
- newReachable,
- newNotAssigned,
- newVariableInfo,
- );
+ first, second, newReachable, newVariableInfo);
}
/// Joins two "variable info" maps. See [join] for details.
@@ -1161,28 +1075,20 @@
/// Creates a new [FlowModel] object, unless it is equivalent to either
/// [first] or [second], in which case one of those objects is re-used.
static FlowModel<Variable, Type> _identicalOrNew<Variable, Type>(
- FlowModel<Variable, Type> first,
- FlowModel<Variable, Type> second,
- bool newReachable,
- _VariableSet<Variable> newNotAssigned,
- Map<Variable, VariableModel<Type>> newVariableInfo,
- ) {
+ FlowModel<Variable, Type> first,
+ FlowModel<Variable, Type> second,
+ bool newReachable,
+ Map<Variable, VariableModel<Type>> newVariableInfo) {
if (first.reachable == newReachable &&
- identical(first.notAssigned, newNotAssigned) &&
identical(first.variableInfo, newVariableInfo)) {
return first;
}
if (second.reachable == newReachable &&
- identical(second.notAssigned, newNotAssigned) &&
identical(second.variableInfo, newVariableInfo)) {
return second;
}
- return new FlowModel<Variable, Type>._(
- newReachable,
- newNotAssigned,
- newVariableInfo,
- );
+ return new FlowModel<Variable, Type>._(newReachable, newVariableInfo);
}
/// Determines whether the given "variableInfo" maps are equivalent.
@@ -1199,13 +1105,9 @@
if (p2Value != null) return false;
} else {
if (p2Value == null) return false;
- Type p1Type = p1Value.promotedType;
- Type p2Type = p2Value.promotedType;
- if (p1Type == null) {
- if (p2Type != null) return false;
- } else {
- if (p2Type == null) return false;
- if (!typeOperations.isSameType(p1Type, p2Type)) return false;
+ if (!VariableModel._variableModelsEqual<Type>(
+ typeOperations, p1Value, p2Value)) {
+ return false;
}
}
}
@@ -1259,12 +1161,16 @@
/// is not promoted.
final Type promotedType;
- VariableModel(this.promotedType);
+ /// Indicates whether the variable has definitely been assigned.
+ final bool assigned;
+
+ VariableModel(this.promotedType, this.assigned);
@override
bool operator ==(Object other) {
return other is VariableModel<Type> &&
- this.promotedType == other.promotedType;
+ this.promotedType == other.promotedType &&
+ this.assigned == other.assigned;
}
/// Returns an updated model reflect a control path that is known to have
@@ -1274,27 +1180,31 @@
VariableModel<Type> otherModel, bool unsafe) {
Type thisType = promotedType;
Type otherType = otherModel?.promotedType;
+ bool newAssigned = assigned || otherModel.assigned;
if (!unsafe) {
if (otherType != null &&
(thisType == null ||
typeOperations.isSubtypeOf(otherType, thisType))) {
- return _identicalOrNew(this, otherModel, otherType);
+ return _identicalOrNew(this, otherModel, otherType, newAssigned);
}
}
- if (thisType != null) {
- return _identicalOrNew(this, otherModel, thisType);
- } else {
- return _identicalOrNew(this, otherModel, null);
- }
+ return _identicalOrNew(this, otherModel, thisType, newAssigned);
}
@override
- String toString() => 'VariableModel($promotedType)';
+ String toString() => 'VariableModel($promotedType, $assigned)';
/// Returns a new [VariableModel] where the promoted type is replaced with
/// [promotedType].
VariableModel<Type> withPromotedType(Type promotedType) =>
- new VariableModel<Type>(promotedType);
+ new VariableModel<Type>(promotedType, assigned);
+
+ /// Returns a new [VariableModel] reflecting the fact that the variable was
+ /// just written to.
+ VariableModel<Type> write() {
+ if (promotedType == null && assigned) return this;
+ return new VariableModel<Type>(null, true);
+ }
/// Joins two variable models. See [FlowModel.join] for details.
static VariableModel<Type> join<Type>(
@@ -1315,115 +1225,39 @@
} else {
newPromotedType = null;
}
- return _identicalOrNew(first, second, newPromotedType);
+ bool newAssigned = first.assigned && second.assigned;
+ return _identicalOrNew(first, second, newPromotedType, newAssigned);
}
/// Creates a new [VariableModel] object, unless it is equivalent to either
/// [first] or [second], in which case one of those objects is re-used.
static VariableModel<Type> _identicalOrNew<Type>(VariableModel<Type> first,
- VariableModel<Type> second, Type newPromotedType) {
- if (identical(first.promotedType, newPromotedType)) {
+ VariableModel<Type> second, Type newPromotedType, bool newAssigned) {
+ if (identical(first.promotedType, newPromotedType) &&
+ first.assigned == newAssigned) {
return first;
- } else if (identical(second.promotedType, newPromotedType)) {
+ } else if (identical(second.promotedType, newPromotedType) &&
+ second.assigned == newAssigned) {
return second;
} else {
- return new VariableModel<Type>(newPromotedType);
+ return new VariableModel<Type>(newPromotedType, newAssigned);
}
}
-}
-/// List based immutable set of variables.
-class _VariableSet<Variable> {
- final List<Variable> variables;
-
- _VariableSet._(this.variables);
-
- _VariableSet<Variable> add(Variable addedVariable) {
- if (contains(addedVariable)) {
- return this;
+ /// Determines whether the given variable models are equivalent.
+ static bool _variableModelsEqual<Type>(
+ TypeOperations<Object, Type> typeOperations,
+ VariableModel<Type> model1,
+ VariableModel<Type> model2) {
+ Type p1Type = model1.promotedType;
+ Type p2Type = model2.promotedType;
+ if (p1Type == null) {
+ if (p2Type != null) return false;
+ } else {
+ if (p2Type == null) return false;
+ if (!typeOperations.isSameType(p1Type, p2Type)) return false;
}
-
- int length = variables.length;
- List<Variable> newVariables = new List<Variable>(length + 1);
- for (int i = 0; i < length; ++i) {
- newVariables[i] = variables[i];
- }
- newVariables[length] = addedVariable;
- return new _VariableSet._(newVariables);
- }
-
- _VariableSet<Variable> addAll(Iterable<Variable> variables) {
- _VariableSet<Variable> result = this;
- for (Variable variable in variables) {
- result = result.add(variable);
- }
- return result;
- }
-
- bool contains(Variable variable) {
- int length = variables.length;
- for (int i = 0; i < length; ++i) {
- if (identical(variables[i], variable)) {
- return true;
- }
- }
- return false;
- }
-
- _VariableSet<Variable> intersect({
- _VariableSet<Variable> empty,
- _VariableSet<Variable> other,
- }) {
- if (identical(other, empty)) return empty;
- if (identical(this, other)) return this;
-
- // TODO(scheglov) optimize
- List<Variable> newVariables =
- variables.toSet().intersection(other.variables.toSet()).toList();
-
- if (newVariables.isEmpty) return empty;
- return new _VariableSet._(newVariables);
- }
-
- _VariableSet<Variable> remove(
- _VariableSet<Variable> empty,
- Variable removedVariable,
- ) {
- if (!contains(removedVariable)) {
- return this;
- }
-
- int length = variables.length;
- if (length == 1) {
- return empty;
- }
-
- List<Variable> newVariables = new List<Variable>(length - 1);
- int newIndex = 0;
- for (int i = 0; i < length; ++i) {
- Variable variable = variables[i];
- if (!identical(variable, removedVariable)) {
- newVariables[newIndex++] = variable;
- }
- }
-
- return new _VariableSet._(newVariables);
- }
-
- @override
- String toString() => variables.isEmpty ? '{}' : '{ ${variables.join(', ')} }';
-
- _VariableSet<Variable> union(_VariableSet<Variable> other) {
- if (other.variables.isEmpty) {
- return this;
- }
-
- _VariableSet<Variable> result = this;
- List<Variable> otherVariables = other.variables;
- for (int i = 0; i < otherVariables.length; ++i) {
- Variable otherVariable = otherVariables[i];
- result = result.add(otherVariable);
- }
- return result;
+ if (model1.assigned != model2.assigned) return false;
+ return true;
}
}
diff --git a/pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart b/pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart
index 3a90834..29fafc1 100644
--- a/pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart
+++ b/pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart
@@ -551,7 +551,6 @@
});
group('State', () {
- var emptySet = FlowModel<_Var, _Type>(true).notAssigned;
var intVar = _Var('x', _Type('int'));
var intQVar = _Var('x', _Type('int?'));
var objectQVar = _Var('x', _Type('Object?'));
@@ -568,7 +567,6 @@
var s = initial.setReachable(newReachability);
expect(s, isNot(same(initial)));
expect(s.reachable, newReachability);
- expect(s.notAssigned, same(initial.notAssigned));
expect(s.variableInfo, same(initial.variableInfo));
}
@@ -582,24 +580,21 @@
// By default, added variables are considered unassigned.
var s1 = FlowModel<_Var, _Type>(true);
var s2 = s1.add(intVar);
- expect(s2.notAssigned.contains(intVar), true);
expect(s2.reachable, true);
- expect(s2.variableInfo, {intVar: VariableModel<_Type>(null)});
+ expect(s2.variableInfo, {intVar: VariableModel<_Type>(null, false)});
});
test('unassigned', () {
var s1 = FlowModel<_Var, _Type>(true);
var s2 = s1.add(intVar, assigned: false);
- expect(s2.notAssigned.contains(intVar), true);
expect(s2.reachable, true);
- expect(s2.variableInfo, {intVar: VariableModel<_Type>(null)});
+ expect(s2.variableInfo, {intVar: VariableModel<_Type>(null, false)});
});
test('assigned', () {
var s1 = FlowModel<_Var, _Type>(true);
var s2 = s1.add(intVar, assigned: true);
- expect(s2.notAssigned.contains(intVar), false);
- expect(s2.variableInfo, {intVar: VariableModel<_Type>(null)});
+ expect(s2.variableInfo, {intVar: VariableModel<_Type>(null, true)});
});
});
@@ -630,10 +625,9 @@
var s1 = FlowModel<_Var, _Type>(true).add(intQVar);
var s2 = s1.promote(h, intQVar, _Type('int'));
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
_Type.allowComparisons(() {
- expect(
- s2.variableInfo, {intQVar: VariableModel<_Type>(_Type('int'))});
+ expect(s2.variableInfo,
+ {intQVar: VariableModel<_Type>(_Type('int'), false)});
});
});
@@ -671,10 +665,9 @@
.promote(h, objectQVar, _Type('int?'));
var s2 = s1.promote(h, objectQVar, _Type('int'));
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
_Type.allowComparisons(() {
expect(s2.variableInfo,
- {objectQVar: VariableModel<_Type>(_Type('int'))});
+ {objectQVar: VariableModel<_Type>(_Type('int'), false)});
});
});
});
@@ -684,17 +677,16 @@
test('unchanged', () {
var h = _Harness();
var s1 = FlowModel<_Var, _Type>(true).add(objectQVar, assigned: true);
- var s2 = s1.write(h, emptySet, objectQVar);
+ var s2 = s1.write(h, objectQVar);
expect(s2, same(s1));
});
test('marks as assigned', () {
var h = _Harness();
var s1 = FlowModel<_Var, _Type>(true).add(objectQVar, assigned: false);
- var s2 = s1.write(h, emptySet, objectQVar);
+ var s2 = s1.write(h, objectQVar);
expect(s2.reachable, true);
- expect(s2.notAssigned.contains(objectQVar), false);
- expect(s2.variableInfo, same(s1.variableInfo));
+ expect(s2.variableInfo[objectQVar], VariableModel<_Type>(null, true));
});
test('un-promotes', () {
@@ -703,10 +695,9 @@
.add(objectQVar, assigned: true)
.promote(h, objectQVar, _Type('int'));
expect(s1.variableInfo, contains(objectQVar));
- var s2 = s1.write(h, emptySet, objectQVar);
+ var s2 = s1.write(h, objectQVar);
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
- expect(s2.variableInfo, {objectQVar: VariableModel<_Type>(null)});
+ expect(s2.variableInfo, {objectQVar: VariableModel<_Type>(null, true)});
});
});
@@ -723,8 +714,9 @@
var s1 = FlowModel<_Var, _Type>(true).add(intQVar);
var s2 = s1.markNonNullable(h, intQVar);
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
- expect(s2.variableInfo[intQVar].promotedType.type, 'int');
+ _Type.allowComparisons(() {
+ expect(s2.variableInfo[intQVar], VariableModel(_Type('int'), false));
+ });
});
test('promoted -> unchanged', () {
@@ -743,10 +735,9 @@
.promote(h, objectQVar, _Type('int?'));
var s2 = s1.markNonNullable(h, objectQVar);
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
_Type.allowComparisons(() {
expect(s2.variableInfo,
- {objectQVar: VariableModel<_Type>(_Type('int'))});
+ {objectQVar: VariableModel<_Type>(_Type('int'), false)});
});
});
});
@@ -771,11 +762,10 @@
.promote(h, intQVar, _Type('int'));
var s2 = s1.removePromotedAll([intQVar], null);
expect(s2.reachable, true);
- expect(s2.notAssigned, same(s1.notAssigned));
_Type.allowComparisons(() {
expect(s2.variableInfo, {
- objectQVar: VariableModel<_Type>(_Type('int')),
- intQVar: VariableModel<_Type>(null)
+ objectQVar: VariableModel<_Type>(_Type('int'), false),
+ intQVar: VariableModel<_Type>(null, false)
});
});
});
@@ -786,14 +776,10 @@
var h = _Harness();
var reachable = FlowModel<_Var, _Type>(true);
var unreachable = reachable.setReachable(false);
- expect(
- reachable.restrict(h, emptySet, reachable, Set()), same(reachable));
- expect(reachable.restrict(h, emptySet, unreachable, Set()),
- same(unreachable));
- expect(unreachable.restrict(h, emptySet, unreachable, Set()),
- same(unreachable));
- expect(unreachable.restrict(h, emptySet, unreachable, Set()),
- same(unreachable));
+ expect(reachable.restrict(h, reachable, Set()), same(reachable));
+ expect(reachable.restrict(h, unreachable, Set()), same(unreachable));
+ expect(unreachable.restrict(h, unreachable, Set()), same(unreachable));
+ expect(unreachable.restrict(h, unreachable, Set()), same(unreachable));
});
test('assignments', () {
@@ -803,13 +789,13 @@
var c = _Var('c', _Type('int'));
var d = _Var('d', _Type('int'));
var s0 = FlowModel<_Var, _Type>(true).add(a).add(b).add(c).add(d);
- var s1 = s0.write(h, emptySet, a).write(h, emptySet, b);
- var s2 = s0.write(h, emptySet, a).write(h, emptySet, c);
- var result = s1.restrict(h, emptySet, s2, Set());
- expect(result.notAssigned.contains(a), false);
- expect(result.notAssigned.contains(b), false);
- expect(result.notAssigned.contains(c), false);
- expect(result.notAssigned.contains(d), true);
+ var s1 = s0.write(h, a).write(h, b);
+ var s2 = s0.write(h, a).write(h, c);
+ var result = s1.restrict(h, s2, Set());
+ expect(result.variableInfo[a].assigned, true);
+ expect(result.variableInfo[b].assigned, true);
+ expect(result.variableInfo[c].assigned, true);
+ expect(result.variableInfo[d].assigned, false);
});
test('promotion', () {
@@ -820,8 +806,7 @@
var s0 = FlowModel<_Var, _Type>(true).add(x, assigned: true);
var s1 = thisType == null ? s0 : s0.promote(h, x, _Type(thisType));
var s2 = otherType == null ? s0 : s0.promote(h, x, _Type(otherType));
- var result =
- s1.restrict(h, emptySet, s2, unsafe ? [x].toSet() : Set());
+ var result = s1.restrict(h, s2, unsafe ? [x].toSet() : Set());
if (expectedType == null) {
expect(result.variableInfo, contains(x));
expect(result.variableInfo[x].promotedType, isNull);
@@ -849,10 +834,10 @@
var x = _Var('x', _Type('Object?'));
var s0 = FlowModel<_Var, _Type>(true);
var s1 = s0.add(x, assigned: true);
- expect(s0.restrict(h, emptySet, s1, {}), same(s0));
- expect(s0.restrict(h, emptySet, s1, {x}), same(s0));
- expect(s1.restrict(h, emptySet, s0, {}), same(s1));
- expect(s1.restrict(h, emptySet, s0, {x}), same(s1));
+ expect(s0.restrict(h, s1, {}), same(s0));
+ expect(s0.restrict(h, s1, {x}), same(s0));
+ expect(s1.restrict(h, s0, {}), same(s1));
+ expect(s1.restrict(h, s0, {x}), same(s1));
});
});
});
@@ -865,7 +850,7 @@
var stringType = _Type('String');
const emptyMap = <Null, VariableModel<Null>>{};
- VariableModel<_Type> model(_Type type) => VariableModel<_Type>(type);
+ VariableModel<_Type> model(_Type type) => VariableModel<_Type>(type, true);
group('without input reuse', () {
test('promoted with unpromoted', () {
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 050c481..f94657b 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -865,6 +865,7 @@
encoder
encodes
encoding
+encounter
encountered
encounters
end
@@ -2178,6 +2179,7 @@
refers
refine
refinement
+reflecting
regardless
region
regions
@@ -2201,6 +2203,7 @@
relative
release
relevant
+relied
relies
reloads
rely