Index: sdk/lib/_internal/compiler/implementation/resolution/members.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
index 14d0a436fb72123ed2a52423938779b5eaaea907..2b2db688c1d0f6a90f4f1bcad629ab1a9318180e 100644 |
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
@@ -39,6 +39,19 @@ abstract class TreeElements { |
/// Register additional dependencies required by [currentElement]. |
/// For example, elements that are used by a backend. |
void registerDependency(Element element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated anywhere in its |
+ /// scope. |
+ bool isPotentiallyMutated(VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated in [node]. |
+ bool isPotentiallyMutatedIn(Node node, VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated in a closure. |
+ bool isPotentiallyMutatedInClosure(VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is accessed by a closure in [node]. |
+ bool isAccessedByClosureIn(Node node, VariableElement element); |
} |
class TreeElementMapping implements TreeElements { |
@@ -48,6 +61,13 @@ class TreeElementMapping implements TreeElements { |
final Set<Node> superUses = new Set<Node>(); |
final Set<Element> otherDependencies = new Set<Element>(); |
final Map<Node, Constant> constants = new Map<Node, Constant>(); |
+ final Set<VariableElement> potentiallyMutated = new Set<VariableElement>(); |
+ final Map<Node, Set<VariableElement>> potentiallyMutatedIn = |
+ new Map<Node, Set<VariableElement>>(); |
+ final Set<VariableElement> potentiallyMutatedInClosure = |
+ new Set<VariableElement>(); |
+ final Map<Node, Set<VariableElement>> accessedByClosureIn = |
+ new Map<Node, Set<VariableElement>>(); |
final int hashCode = ++hashCodeCounter; |
static int hashCodeCounter = 0; |
@@ -158,6 +178,42 @@ class TreeElementMapping implements TreeElements { |
otherDependencies.add(element.implementation); |
} |
+ bool isPotentiallyMutated(VariableElement element) { |
+ return potentiallyMutated.contains(element); |
+ } |
+ |
+ void setPotentiallyMutated(VariableElement element) { |
+ potentiallyMutated.add(element); |
+ } |
+ |
+ bool isPotentiallyMutatedIn(Node node, VariableElement element) { |
+ Set<Element> mutatedIn = potentiallyMutatedIn[node]; |
+ return mutatedIn != null && mutatedIn.contains(element); |
+ } |
+ |
+ void setPotentiallyMutatedIn(Node node, VariableElement element) { |
+ potentiallyMutatedIn.putIfAbsent( |
+ node, () => new Set<VariableElement>()).add(element); |
+ } |
+ |
+ bool isPotentiallyMutatedInClosure(VariableElement element) { |
+ return potentiallyMutatedInClosure.contains(element); |
+ } |
+ |
+ void setPotentiallyMutatedInClosure(VariableElement element) { |
+ potentiallyMutatedInClosure.add(element); |
+ } |
+ |
+ bool isAccessedByClosureIn(Node node, VariableElement element) { |
+ Set<Element> accessedIn = accessedByClosureIn[node]; |
+ return accessedIn != null && accessedIn.contains(element); |
+ } |
+ |
+ void setAccessedByClosureIn(Node node, VariableElement element) { |
+ accessedByClosureIn.putIfAbsent( |
+ node, () => new Set<VariableElement>()).add(element); |
+ } |
+ |
String toString() => 'TreeElementMapping($currentElement)'; |
} |
@@ -1845,6 +1901,16 @@ class ResolverVisitor extends MappingVisitor<Element> { |
/// error when verifying that all final variables are initialized. |
bool allowFinalWithoutInitializer = false; |
+ /// The nodes for which variable access and mutation must be registered in |
+ /// order to determine when the static type of variables types is promoted. |
+ Link<Node> promotionScope = const Link<Node>(); |
+ |
+ bool isPotentiallyMutableTarget(Element target) { |
+ if (target == null) return false; |
+ return (target.isVariable() || target.isParameter()) && |
+ !(target.modifiers.isFinal() || target.modifiers.isConst()); |
+ } |
+ |
// TODO(ahe): Find a way to share this with runtime implementation. |
static final RegExp symbolValidationPattern = |
new RegExp(r'^(?:[a-zA-Z$][a-zA-Z$0-9_]*\.)*(?:[a-zA-Z$][a-zA-Z$0-9_]*=?|' |
@@ -1943,6 +2009,13 @@ class ResolverVisitor extends MappingVisitor<Element> { |
return result; |
} |
+ doInPromotionScope(Node node, action()) { |
+ promotionScope = promotionScope.prepend(node); |
+ var result = action(); |
+ promotionScope = promotionScope.tail; |
+ return result; |
+ } |
+ |
visitInStaticContext(Node node) { |
inStaticContext(() => visit(node)); |
} |
@@ -2185,8 +2258,9 @@ class ResolverVisitor extends MappingVisitor<Element> { |
} |
visitIf(If node) { |
- visit(node.condition); |
- visitIn(node.thenPart, new BlockScope(scope)); |
+ doInPromotionScope(node.condition.expression, () => visit(node.condition)); |
+ doInPromotionScope(node.thenPart, |
+ () => visitIn(node.thenPart, new BlockScope(scope))); |
visitIn(node.elsePart, new BlockScope(scope)); |
} |
@@ -2412,7 +2486,12 @@ class ResolverVisitor extends MappingVisitor<Element> { |
visitSend(Send node) { |
bool oldSendIsMemberAccess = sendIsMemberAccess; |
sendIsMemberAccess = node.isPropertyAccess || node.isCall; |
- Element target = resolveSend(node); |
+ Element target; |
+ if (node.isLogicalAnd) { |
+ target = doInPromotionScope(node.receiver, () => resolveSend(node)); |
+ } else { |
+ target = resolveSend(node); |
+ } |
sendIsMemberAccess = oldSendIsMemberAccess; |
if (target != null |
@@ -2452,12 +2531,21 @@ class ResolverVisitor extends MappingVisitor<Element> { |
// Don't try to make constants of calls to type literals. |
analyzeConstant(node, isConst: !node.isCall); |
} |
+ if (isPotentiallyMutableTarget(target)) { |
+ if (enclosingElement != target.enclosingElement) { |
+ for (Node scope in promotionScope) { |
+ mapping.setAccessedByClosureIn(scope, target); |
+ } |
+ } |
+ } |
} |
bool resolvedArguments = false; |
if (node.isOperator) { |
String operatorString = node.selector.asOperator().source.stringValue; |
if (identical(operatorString, 'is')) { |
+ // TODO(johnniwinther): Use seen type tests to avoid registration of |
+ // mutation/access to unpromoted variables. |
DartType type = |
resolveTypeExpression(node.typeAnnotationFromIsCheckOrCast); |
if (type != null) { |
@@ -2470,6 +2558,10 @@ class ResolverVisitor extends MappingVisitor<Element> { |
compiler.enqueuer.resolution.registerAsCheck(type, mapping); |
} |
resolvedArguments = true; |
+ } else if (identical(operatorString, '&&')) { |
+ doInPromotionScope(node.arguments.head, |
+ () => resolveArguments(node.argumentsNode)); |
+ resolvedArguments = true; |
} |
} |
@@ -2572,6 +2664,15 @@ class ResolverVisitor extends MappingVisitor<Element> { |
node.selector, target.name, MessageKind.CANNOT_RESOLVE_SETTER); |
compiler.backend.registerThrowNoSuchMethod(mapping); |
} |
+ if (isPotentiallyMutableTarget(target)) { |
+ mapping.setPotentiallyMutated(target); |
+ if (enclosingElement != target.enclosingElement) { |
+ mapping.setPotentiallyMutatedInClosure(target); |
+ } |
+ for (Node scope in promotionScope) { |
+ mapping.setPotentiallyMutatedIn(scope, target); |
+ } |
+ } |
} |
visit(node.argumentsNode); |
@@ -3002,7 +3103,9 @@ class ResolverVisitor extends MappingVisitor<Element> { |
} |
visitConditional(Conditional node) { |
- node.visitChildren(this); |
+ doInPromotionScope(node.condition, () => visit(node.condition)); |
+ doInPromotionScope(node.thenExpression, () => visit(node.thenExpression)); |
+ visit(node.elseExpression); |
} |
visitStringInterpolation(StringInterpolation node) { |