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:
Konstantin Shcheglov 2020-03-09 18:24:51 +00:00 committed by commit-bot@chromium.org
parent 9a62df57fa
commit 24c8021f03
12 changed files with 149 additions and 118 deletions

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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) {

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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
}
}
}