mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:59:38 +00:00
Replace initialize() with declare().
Fixes https://github.com/dart-lang/sdk/issues/38791 Change-Id: I954059160c75de91e1e32fe2f82ebfdc5569d947 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138666 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
9a62df57fa
commit
24c8021f03
|
@ -229,16 +229,16 @@ class AssignedVariablesForTesting<Node, Variable>
|
|||
|
||||
/// Information tracked by [AssignedVariables] for a single node.
|
||||
class AssignedVariablesNodeInfo<Variable> {
|
||||
/// The set of local variables that are potentially written in the node.
|
||||
final Set<Variable> _written = new Set<Variable>.identity();
|
||||
|
||||
// The set of local variables that are potentially written in the node.
|
||||
/// The set of local variables for which a potential write is captured by a
|
||||
/// local function or closure inside 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.
|
||||
/// The set of local variables that are declared in 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)';
|
||||
|
@ -341,6 +341,13 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
|
|||
/// ("?:"). [condition] should be the expression preceding the "?".
|
||||
void conditional_thenBegin(Expression condition);
|
||||
|
||||
/// Register a declaration of the [variable] in the current state.
|
||||
/// Should also be called for function parameters.
|
||||
///
|
||||
/// A local variable is [initialized] if its declaration has an initializer.
|
||||
/// A function parameter is always initialized, so [initialized] is `true`.
|
||||
void declare(Variable variable, bool initialized);
|
||||
|
||||
/// Call this method before visiting the body of a "do-while" statement.
|
||||
/// [doStatement] should be the same node that was passed to
|
||||
/// [AssignedVariables.endNode] for the do-while statement.
|
||||
|
@ -494,10 +501,6 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
|
|||
/// - Call [ifStatement_end], passing `true` for `hasElse`.
|
||||
void ifStatement_thenBegin(Expression condition);
|
||||
|
||||
/// Register an initialized declaration of the given [variable] in the current
|
||||
/// state. Should also be called for function parameters.
|
||||
void initialize(Variable variable);
|
||||
|
||||
/// Return whether the [variable] is definitely assigned in the current state.
|
||||
bool isAssigned(Variable variable);
|
||||
|
||||
|
@ -774,6 +777,12 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
|
|||
() => _wrapped.conditional_thenBegin(condition));
|
||||
}
|
||||
|
||||
@override
|
||||
void declare(Variable variable, bool initialized) {
|
||||
_wrap('declare($variable, $initialized)',
|
||||
() => _wrapped.declare(variable, initialized));
|
||||
}
|
||||
|
||||
@override
|
||||
void doStatement_bodyBegin(Statement doStatement) {
|
||||
return _wrap('doStatement_bodyBegin($doStatement)',
|
||||
|
@ -903,11 +912,6 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
|
|||
() => _wrapped.ifStatement_thenBegin(condition));
|
||||
}
|
||||
|
||||
@override
|
||||
void initialize(Variable variable) {
|
||||
_wrap('initialize($variable)', () => _wrapped.initialize(variable));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAssigned(Variable variable) {
|
||||
return _wrap('isAssigned($variable)', () => _wrapped.isAssigned(variable),
|
||||
|
@ -1152,20 +1156,24 @@ class FlowModel<Variable, Type> {
|
|||
}());
|
||||
}
|
||||
|
||||
/// Register a declaration of the [variable].
|
||||
/// Should also be called for function parameters.
|
||||
///
|
||||
/// A local variable is [initialized] if its declaration has an initializer.
|
||||
/// A function parameter is always initialized, so [initialized] is `true`.
|
||||
FlowModel<Variable, Type> declare(Variable variable, bool initialized) {
|
||||
VariableModel<Type> newInfoForVar = _freshVariableInfo;
|
||||
if (initialized) {
|
||||
newInfoForVar = newInfoForVar.initialize();
|
||||
}
|
||||
|
||||
return _updateVariableInfo(variable, newInfoForVar);
|
||||
}
|
||||
|
||||
/// Gets the info for the given [variable], creating it if it doesn't exist.
|
||||
VariableModel<Type> infoFor(Variable variable) =>
|
||||
variableInfo[variable] ?? _freshVariableInfo;
|
||||
|
||||
/// Updates the state to indicate that the given [variable] was initialized.
|
||||
/// The variable is marked as definitely assigned, and any previous type
|
||||
/// promotion is removed.
|
||||
FlowModel<Variable, Type> initialize(Variable variable) {
|
||||
VariableModel<Type> infoForVar = infoFor(variable);
|
||||
VariableModel<Type> newInfoForVar = infoForVar.initialize();
|
||||
if (identical(newInfoForVar, infoForVar)) return this;
|
||||
return _updateVariableInfo(variable, newInfoForVar);
|
||||
}
|
||||
|
||||
/// Updates the state to indicate that the given [writtenVariables] are no
|
||||
/// longer promoted; they are presumed to have their declared types.
|
||||
///
|
||||
|
@ -1245,28 +1253,24 @@ class FlowModel<Variable, Type> {
|
|||
in variableInfo.entries) {
|
||||
Variable variable = entry.key;
|
||||
VariableModel<Type> thisModel = entry.value;
|
||||
VariableModel<Type> otherModel = other.infoFor(variable);
|
||||
VariableModel<Type> otherModel = other.variableInfo[variable];
|
||||
if (otherModel == null) {
|
||||
variableInfoMatchesThis = false;
|
||||
continue;
|
||||
}
|
||||
VariableModel<Type> restricted = thisModel.restrict(
|
||||
typeOperations, otherModel, unsafe.contains(variable));
|
||||
if (!identical(restricted, _freshVariableInfo)) {
|
||||
newVariableInfo[variable] = restricted;
|
||||
}
|
||||
newVariableInfo[variable] = restricted;
|
||||
if (!identical(restricted, thisModel)) variableInfoMatchesThis = false;
|
||||
if (!identical(restricted, otherModel)) variableInfoMatchesOther = false;
|
||||
}
|
||||
for (MapEntry<Variable, VariableModel<Type>> entry
|
||||
in other.variableInfo.entries) {
|
||||
Variable variable = entry.key;
|
||||
if (variableInfo.containsKey(variable)) continue;
|
||||
VariableModel<Type> thisModel = _freshVariableInfo;
|
||||
VariableModel<Type> otherModel = entry.value;
|
||||
VariableModel<Type> restricted = thisModel.restrict(
|
||||
typeOperations, otherModel, unsafe.contains(variable));
|
||||
if (!identical(restricted, _freshVariableInfo)) {
|
||||
newVariableInfo[variable] = restricted;
|
||||
if (variableInfoMatchesOther) {
|
||||
for (Variable variable in other.variableInfo.keys) {
|
||||
if (!variableInfo.containsKey(variable)) {
|
||||
variableInfoMatchesOther = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!identical(restricted, thisModel)) variableInfoMatchesThis = false;
|
||||
if (!identical(restricted, otherModel)) variableInfoMatchesOther = false;
|
||||
}
|
||||
assert(variableInfoMatchesThis ==
|
||||
_variableInfosEqual(newVariableInfo, variableInfo));
|
||||
|
@ -1347,10 +1351,13 @@ class FlowModel<Variable, Type> {
|
|||
/// previous type promotion is removed.
|
||||
FlowModel<Variable, Type> write(Variable variable, Type writtenType,
|
||||
TypeOperations<Variable, Type> typeOperations) {
|
||||
VariableModel<Type> infoForVar = infoFor(variable);
|
||||
VariableModel<Type> infoForVar = variableInfo[variable];
|
||||
if (infoForVar == null) return this;
|
||||
|
||||
VariableModel<Type> newInfoForVar =
|
||||
infoForVar.write(writtenType, typeOperations);
|
||||
if (identical(newInfoForVar, infoForVar)) return this;
|
||||
|
||||
return _updateVariableInfo(variable, newInfoForVar);
|
||||
}
|
||||
|
||||
|
@ -2053,6 +2060,11 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
|
|||
_current = conditionInfo.ifTrue;
|
||||
}
|
||||
|
||||
@override
|
||||
void declare(Variable variable, bool initialized) {
|
||||
_current = _current.declare(variable, initialized);
|
||||
}
|
||||
|
||||
@override
|
||||
void doStatement_bodyBegin(Statement doStatement) {
|
||||
AssignedVariablesNodeInfo<Variable> info =
|
||||
|
@ -2267,11 +2279,6 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
|
|||
_current = conditionInfo.ifTrue;
|
||||
}
|
||||
|
||||
@override
|
||||
void initialize(Variable variable) {
|
||||
_current = _current.initialize(variable);
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAssigned(Variable variable) {
|
||||
return _current.infoFor(variable).assigned;
|
||||
|
@ -2441,10 +2448,10 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
|
|||
_stack.last as _TryContext<Variable, Type>;
|
||||
_current = context._beforeCatch;
|
||||
if (exceptionVariable != null) {
|
||||
_current = _current.initialize(exceptionVariable);
|
||||
_current = _current.declare(exceptionVariable, true);
|
||||
}
|
||||
if (stackTraceVariable != null) {
|
||||
_current = _current.initialize(stackTraceVariable);
|
||||
_current = _current.declare(stackTraceVariable, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -403,7 +403,7 @@ main() {
|
|||
h.declare(y, initialized: true);
|
||||
h.promote(y, 'int');
|
||||
flow.for_conditionBegin(forStatement);
|
||||
flow.initialize(x);
|
||||
flow.declare(x, true);
|
||||
flow.for_bodyBegin(_Statement(), _Expression());
|
||||
flow.for_updaterBegin();
|
||||
flow.for_end();
|
||||
|
@ -697,18 +697,17 @@ main() {
|
|||
test('functionExpression_begin() handles not-yet-seen variables', () {
|
||||
var h = _Harness();
|
||||
var x = h.addVar('x', 'int?');
|
||||
var y = h.addVar('y', 'int?');
|
||||
var functionNode = _Node();
|
||||
h.assignedVariables(
|
||||
(vars) => vars.function(functionNode, () => vars.write(x)));
|
||||
h.run((flow) {
|
||||
h.declare(y, initialized: true);
|
||||
h.promote(y, 'int');
|
||||
flow.functionExpression_begin(functionNode);
|
||||
flow.functionExpression_end();
|
||||
// x is declared after the local function, so the local function
|
||||
// cannot possibly write to x.
|
||||
h.declare(x, initialized: true);
|
||||
h.promote(x, 'int');
|
||||
expect(flow.promotedType(x), isNull);
|
||||
expect(flow.promotedType(x).type, 'int');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1569,7 +1568,7 @@ main() {
|
|||
h.declare(y, initialized: true);
|
||||
h.promote(y, 'int');
|
||||
flow.whileStatement_conditionBegin(whileStatement);
|
||||
flow.initialize(x);
|
||||
flow.declare(x, true);
|
||||
flow.whileStatement_bodyBegin(_Statement(), _Expression());
|
||||
flow.whileStatement_end();
|
||||
});
|
||||
|
@ -1789,17 +1788,27 @@ main() {
|
|||
|
||||
group('write', () {
|
||||
var objectQVar = _Var('x', _Type('Object?'));
|
||||
|
||||
test('without declaration', () {
|
||||
// This should not happen in valid code, but test that we don't crash.
|
||||
var h = _Harness();
|
||||
var s =
|
||||
FlowModel<_Var, _Type>(true).write(objectQVar, _Type('Object?'), h);
|
||||
expect(s.variableInfo[objectQVar], isNull);
|
||||
});
|
||||
|
||||
test('unchanged', () {
|
||||
var h = _Harness();
|
||||
var s1 =
|
||||
FlowModel<_Var, _Type>(true).write(objectQVar, _Type('Object?'), h);
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h);
|
||||
var s2 = s1.write(objectQVar, _Type('Object?'), h);
|
||||
expect(s2, same(s1));
|
||||
});
|
||||
|
||||
test('marks as assigned', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true);
|
||||
var s1 = FlowModel<_Var, _Type>(true).declare(objectQVar, false);
|
||||
var s2 = s1.write(objectQVar, _Type('int?'), h);
|
||||
expect(s2.reachable, true);
|
||||
expect(
|
||||
|
@ -1811,6 +1820,7 @@ main() {
|
|||
test('un-promotes fully', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('int'))
|
||||
.ifTrue;
|
||||
|
@ -1826,6 +1836,7 @@ main() {
|
|||
test('un-promotes partially, when no exact match', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifTrue
|
||||
|
@ -1848,6 +1859,7 @@ main() {
|
|||
test('un-promotes partially, when exact match', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifTrue
|
||||
|
@ -1874,6 +1886,7 @@ main() {
|
|||
test('leaves promoted, when exact match', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifTrue
|
||||
|
@ -1893,6 +1906,7 @@ main() {
|
|||
test('leaves promoted, when writing a subtype', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifTrue
|
||||
|
@ -1912,6 +1926,7 @@ main() {
|
|||
test('Promotes to type of interest when not previously promoted', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifFalse;
|
||||
|
@ -1927,6 +1942,7 @@ main() {
|
|||
test('Promotes to type of interest when previously promoted', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifTrue
|
||||
|
@ -1947,6 +1963,7 @@ main() {
|
|||
() {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('int?'))
|
||||
.ifFalse
|
||||
|
@ -1968,6 +1985,7 @@ main() {
|
|||
() {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifFalse
|
||||
|
@ -1987,6 +2005,7 @@ main() {
|
|||
test('Multiple candidate types of interest; ambiguous', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifFalse
|
||||
|
@ -2006,6 +2025,7 @@ main() {
|
|||
() {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.declare(objectQVar, false)
|
||||
.write(objectQVar, _Type('Object?'), h)
|
||||
.tryPromote(h, objectQVar, _Type('num?'))
|
||||
.ifFalse
|
||||
|
@ -2025,36 +2045,20 @@ main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('initialize', () {
|
||||
group('declare', () {
|
||||
var objectQVar = _Var('x', _Type('Object?'));
|
||||
test('unchanged', () {
|
||||
var s1 = FlowModel<_Var, _Type>(true).initialize(objectQVar);
|
||||
var s2 = s1.initialize(objectQVar);
|
||||
expect(s2, same(s1));
|
||||
|
||||
test('initialized', () {
|
||||
var s = FlowModel<_Var, _Type>(true).declare(objectQVar, true);
|
||||
expect(s.variableInfo, {
|
||||
objectQVar: _matchVariableModel(assigned: true),
|
||||
});
|
||||
});
|
||||
|
||||
test('marks as assigned', () {
|
||||
var s1 = FlowModel<_Var, _Type>(true);
|
||||
var s2 = s1.initialize(objectQVar);
|
||||
expect(s2.reachable, true);
|
||||
expect(
|
||||
s2.infoFor(objectQVar),
|
||||
_matchVariableModel(
|
||||
chain: null, ofInterest: isEmpty, assigned: true));
|
||||
});
|
||||
|
||||
test('un-promotes fully', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
.initialize(objectQVar)
|
||||
.tryPromote(h, objectQVar, _Type('int'))
|
||||
.ifTrue;
|
||||
expect(s1.variableInfo, contains(objectQVar));
|
||||
var s2 = s1.initialize(objectQVar);
|
||||
expect(s2.reachable, true);
|
||||
expect(s2.variableInfo, {
|
||||
objectQVar: _matchVariableModel(
|
||||
chain: null, ofInterest: isEmpty, assigned: true)
|
||||
test('not initialized', () {
|
||||
var s = FlowModel<_Var, _Type>(true).declare(objectQVar, false);
|
||||
expect(s.variableInfo, {
|
||||
objectQVar: _matchVariableModel(assigned: false),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2157,7 +2161,11 @@ main() {
|
|||
var b = _Var('b', _Type('int'));
|
||||
var c = _Var('c', _Type('int'));
|
||||
var d = _Var('d', _Type('int'));
|
||||
var s0 = FlowModel<_Var, _Type>(true);
|
||||
var s0 = FlowModel<_Var, _Type>(true)
|
||||
.declare(a, false)
|
||||
.declare(b, false)
|
||||
.declare(c, false)
|
||||
.declare(d, false);
|
||||
var s1 = s0.write(a, _Type('int'), h).write(b, _Type('int'), h);
|
||||
var s2 = s0.write(a, _Type('int'), h).write(c, _Type('int'), h);
|
||||
var result = s1.restrict(h, s2, Set());
|
||||
|
@ -2173,7 +2181,11 @@ main() {
|
|||
var b = _Var('b', _Type('int'));
|
||||
var c = _Var('c', _Type('int'));
|
||||
var d = _Var('d', _Type('int'));
|
||||
var s0 = FlowModel<_Var, _Type>(true);
|
||||
var s0 = FlowModel<_Var, _Type>(true)
|
||||
.declare(a, false)
|
||||
.declare(b, false)
|
||||
.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 = s0.removePromotedAll([a, c], [a, c]);
|
||||
|
@ -2189,7 +2201,9 @@ main() {
|
|||
List<String> expectedChain) {
|
||||
var h = _Harness();
|
||||
var x = _Var('x', _Type('Object?'));
|
||||
var s0 = FlowModel<_Var, _Type>(true).write(x, _Type('Object?'), h);
|
||||
var s0 = FlowModel<_Var, _Type>(true)
|
||||
.declare(x, false)
|
||||
.write(x, _Type('Object?'), h);
|
||||
var s1 = thisType == null
|
||||
? s0
|
||||
: s0.tryPromote(h, x, _Type(thisType)).ifTrue;
|
||||
|
@ -2241,8 +2255,9 @@ main() {
|
|||
List<String> inFinally, List<String> expectedResult) {
|
||||
var h = _Harness();
|
||||
var x = _Var('x', _Type('Object?'));
|
||||
var initialModel =
|
||||
FlowModel<_Var, _Type>(true).write(x, _Type('Object?'), h);
|
||||
var initialModel = FlowModel<_Var, _Type>(true)
|
||||
.declare(x, false)
|
||||
.write(x, _Type('Object?'), h);
|
||||
for (var t in before) {
|
||||
initialModel = initialModel.tryPromote(h, x, _Type(t)).ifTrue;
|
||||
}
|
||||
|
@ -2294,11 +2309,11 @@ main() {
|
|||
var h = _Harness();
|
||||
var x = _Var('x', _Type('Object?'));
|
||||
var s0 = FlowModel<_Var, _Type>(true);
|
||||
var s1 = s0.write(x, _Type('Object?'), h);
|
||||
expect(s0.restrict(h, s1, {}), same(s1));
|
||||
expect(s0.restrict(h, s1, {x}), same(s1));
|
||||
expect(s1.restrict(h, s0, {}), same(s1));
|
||||
expect(s1.restrict(h, s0, {x}), same(s1));
|
||||
var s1 = s0.declare(x, false).write(x, _Type('Object?'), h);
|
||||
expect(s0.restrict(h, s1, {}), same(s0));
|
||||
expect(s0.restrict(h, s1, {x}), same(s0));
|
||||
expect(s1.restrict(h, s0, {}), same(s0));
|
||||
expect(s1.restrict(h, s0, {x}), same(s0));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2850,9 +2865,7 @@ class _Harness implements TypeOperations<_Var, _Type> {
|
|||
this, _assignedVariables);
|
||||
|
||||
void declare(_Var v, {@required bool initialized}) {
|
||||
if (initialized) {
|
||||
_flow.initialize(v);
|
||||
}
|
||||
_flow.declare(v, initialized);
|
||||
}
|
||||
|
||||
/// Creates a [LazyExpression] representing an `== null` check performed on
|
||||
|
|
|
@ -22,12 +22,16 @@ void isType_mutatedInClosure() {
|
|||
void isType_mutatedInclosure2() {
|
||||
void g(Object x) {
|
||||
if (x is String) {
|
||||
x;
|
||||
/*String*/ x;
|
||||
}
|
||||
|
||||
void h() {
|
||||
x = 42;
|
||||
}
|
||||
|
||||
if (x is String) {
|
||||
x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ void nested_closures(Function([dynamic]) f) {
|
|||
void inner(Object x) {
|
||||
if (x is String) {
|
||||
f();
|
||||
// TODO(paulberry): x should be promoted here.
|
||||
// See https://github.com/dart-lang/sdk/issues/38791
|
||||
x;
|
||||
/*String*/ x;
|
||||
}
|
||||
f(() {
|
||||
if (x is String) {
|
||||
|
|
|
@ -125,14 +125,14 @@ class FlowAnalysisHelper {
|
|||
}
|
||||
|
||||
void executableDeclaration_enter(
|
||||
Declaration node, FormalParameterList parameters, bool isClosure) {
|
||||
AstNode node, FormalParameterList parameters, bool isClosure) {
|
||||
if (isClosure) {
|
||||
flow.functionExpression_begin(node);
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
for (var parameter in parameters.parameters) {
|
||||
flow.initialize(parameter.declaredElement);
|
||||
flow.declare(parameter.declaredElement, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,9 +224,7 @@ class FlowAnalysisHelper {
|
|||
var variables = node.variables;
|
||||
for (var i = 0; i < variables.length; ++i) {
|
||||
var variable = variables[i];
|
||||
if (variable.initializer != null) {
|
||||
flow.initialize(variable.declaredElement);
|
||||
}
|
||||
flow.declare(variable.declaredElement, variable.initializer != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,10 @@ class ForResolver {
|
|||
loopVariableElement.type = elementType;
|
||||
}
|
||||
|
||||
if (loopVariable != null) {
|
||||
_flowAnalysis?.flow?.declare(loopVariable.declaredElement, true);
|
||||
}
|
||||
|
||||
_flowAnalysis?.flow?.forEach_bodyBegin(
|
||||
node,
|
||||
identifierElement is VariableElement
|
||||
|
|
|
@ -1106,7 +1106,7 @@ class ResolverVisitor extends ScopedVisitor {
|
|||
|
||||
if (_flowAnalysis != null) {
|
||||
if (!isFunctionDeclaration) {
|
||||
_flowAnalysis.flow.functionExpression_begin(node);
|
||||
_flowAnalysis.executableDeclaration_enter(node, node.parameters, true);
|
||||
}
|
||||
} else {
|
||||
_promoteManager.enterFunctionBody(body);
|
||||
|
|
|
@ -895,12 +895,12 @@ class BodyBuilder extends ScopeListener<JumpTarget>
|
|||
|
||||
FunctionBuilder builder = member;
|
||||
if (extensionThis != null) {
|
||||
typeInferrer?.flowAnalysis?.initialize(extensionThis);
|
||||
typeInferrer?.flowAnalysis?.declare(extensionThis, true);
|
||||
}
|
||||
if (formals?.parameters != null) {
|
||||
for (int i = 0; i < formals.parameters.length; i++) {
|
||||
FormalParameterBuilder parameter = formals.parameters[i];
|
||||
typeInferrer?.flowAnalysis?.initialize(parameter.variable);
|
||||
typeInferrer?.flowAnalysis?.declare(parameter.variable, true);
|
||||
}
|
||||
for (int i = 0; i < formals.parameters.length; i++) {
|
||||
FormalParameterBuilder parameter = formals.parameters[i];
|
||||
|
@ -1373,7 +1373,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
|
|||
if (formals != null) {
|
||||
for (int i = 0; i < formals.length; i++) {
|
||||
FormalParameterBuilder parameter = formals[i];
|
||||
typeInferrer?.flowAnalysis?.initialize(parameter.variable);
|
||||
typeInferrer?.flowAnalysis?.declare(parameter.variable, true);
|
||||
}
|
||||
}
|
||||
if (_initializers != null) {
|
||||
|
|
|
@ -791,6 +791,7 @@ class InferenceVisitor
|
|||
|
||||
// This is matched by the call to [forEach_end] in
|
||||
// [inferElement], [inferMapEntry] or [inferForInStatement].
|
||||
inferrer.flowAnalysis.declare(variable, true);
|
||||
inferrer.flowAnalysis.forEach_bodyBegin(node, variable, variable.type);
|
||||
|
||||
VariableDeclaration tempVariable =
|
||||
|
@ -1049,6 +1050,7 @@ class InferenceVisitor
|
|||
@override
|
||||
StatementInferenceResult visitFunctionDeclaration(
|
||||
covariant FunctionDeclarationImpl node) {
|
||||
inferrer.flowAnalysis.declare(node.variable, true);
|
||||
inferrer.flowAnalysis.functionExpression_begin(node);
|
||||
inferrer.inferMetadataKeepingHelper(
|
||||
node.variable, node.variable.annotations);
|
||||
|
@ -5372,8 +5374,9 @@ class InferenceVisitor
|
|||
inferredType = inferrer.inferDeclarationType(
|
||||
initializerResult.inferredType,
|
||||
forSyntheticVariable: node.name == null);
|
||||
inferrer.flowAnalysis.initialize(node);
|
||||
inferrer.flowAnalysis.declare(node, true);
|
||||
} else {
|
||||
inferrer.flowAnalysis.declare(node, false);
|
||||
inferredType = const DynamicType();
|
||||
}
|
||||
if (node.isImplicitlyTyped) {
|
||||
|
|
|
@ -2462,7 +2462,7 @@ class TypeInferrerImpl implements TypeInferrer {
|
|||
function.positionalParameters;
|
||||
for (int i = 0; i < positionalParameters.length; i++) {
|
||||
VariableDeclaration parameter = positionalParameters[i];
|
||||
flowAnalysis.initialize(parameter);
|
||||
flowAnalysis.declare(parameter, true);
|
||||
inferMetadataKeepingHelper(parameter, parameter.annotations);
|
||||
if (parameter.initializer != null) {
|
||||
ExpressionInferenceResult initializerResult = inferExpression(
|
||||
|
@ -2472,7 +2472,7 @@ class TypeInferrerImpl implements TypeInferrer {
|
|||
}
|
||||
}
|
||||
for (VariableDeclaration parameter in function.namedParameters) {
|
||||
flowAnalysis.initialize(parameter);
|
||||
flowAnalysis.declare(parameter, true);
|
||||
inferMetadataKeepingHelper(parameter, parameter.annotations);
|
||||
ExpressionInferenceResult initializerResult =
|
||||
inferExpression(parameter.initializer, parameter.type, !isTopLevel);
|
||||
|
|
|
@ -1593,6 +1593,9 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
assert(_flowAnalysis != null);
|
||||
}
|
||||
try {
|
||||
if (declaredElement is PromotableElement) {
|
||||
_flowAnalysis.declare(declaredElement, initializer != null);
|
||||
}
|
||||
if (initializer == null) {
|
||||
// For top level variables and static fields, we have to generate an
|
||||
// implicit assignment of `null`. For instance fields, this is done
|
||||
|
@ -1606,9 +1609,6 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
type.node, ImplicitNullInitializerOrigin(source, node));
|
||||
}
|
||||
} else {
|
||||
if (declaredElement is PromotableElement) {
|
||||
_flowAnalysis.initialize(declaredElement);
|
||||
}
|
||||
var destinationType = getOrComputeElementType(declaredElement);
|
||||
_handleAssignment(initializer, destinationType: destinationType);
|
||||
}
|
||||
|
@ -1652,7 +1652,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
void _addParametersToFlowAnalysis(FormalParameterList parameters) {
|
||||
if (parameters != null) {
|
||||
for (var parameter in parameters.parameters) {
|
||||
_flowAnalysis.initialize(parameter.declaredElement);
|
||||
_flowAnalysis.declare(parameter.declaredElement, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1703,7 +1703,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
_assignedVariables);
|
||||
if (parameters != null) {
|
||||
for (var parameter in parameters.parameters) {
|
||||
_flowAnalysis.initialize(parameter.declaredElement);
|
||||
_flowAnalysis.declare(parameter.declaredElement, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
void f() {
|
||||
void g(Object x) {
|
||||
if (x is String) {
|
||||
x.length; //# none: compile-time error
|
||||
x.length; //# 01: ok
|
||||
}
|
||||
|
||||
void h() {
|
||||
x = 42;
|
||||
}
|
||||
|
||||
if (x is String) {
|
||||
x.length; //# 02: compile-time error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue