Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Unified Diff: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java

Issue 10871063: Infer and merge variable types with multiple assignments in conditional statements (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
index 92f65afe2754b71240de1d29ff3aa8599ea02416..700c08723921ce6f348ee43a9e89a210ce4f8d0d 100644
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
@@ -183,6 +183,12 @@ public class TypeAnalyzer implements DartCompilationPhase {
private final boolean suppressSdkWarnings;
private final boolean typeChecksForInferredTypes;
private final Map<DartBlock, VariableElementsRestorer> restoreOnBlockExit = Maps.newHashMap();
+ /**
+ * When we see variable assignment, we remember here old {@link Type} (if not done already) and
+ * set new {@link Type} into {@link VariableElement}. On the exit from basic block we remove
+ * first element and merge new types with old types.
+ */
+ private final LinkedList<Map<VariableElement, Type>> blockOldTypes = Lists.newLinkedList();
/**
* Keeps track of the number of nested catches, used to detect re-throws
@@ -215,6 +221,11 @@ public class TypeAnalyzer implements DartCompilationPhase {
void setCurrentClass(InterfaceType type) {
currentClass = type;
}
+
+ @VisibleForTesting
+ void pushBasicBlockContext() {
+ blockOldTypes.addFirst(Maps.<VariableElement, Type>newHashMap());
+ }
private InterfaceType getCurrentClass() {
return currentClass;
@@ -356,10 +367,10 @@ public class TypeAnalyzer implements DartCompilationPhase {
switch (operator) {
case ASSIGN: {
Type rhs = nonVoidTypeOf(rhsNode);
- checkPropagatedTypeCompatible(lhsNode, rhs);
if (!hasInferredType(lhsNode)) {
checkAssignable(rhsNode, lhs, rhs);
}
+ setVariableElementType(lhsNode.getElement(), rhs);
checkAssignableElement(lhsNode);
// if cascade, then use type of "lhs" qualifier
if (lhsNode instanceof DartPropertyAccess) {
@@ -572,17 +583,20 @@ public class TypeAnalyzer implements DartCompilationPhase {
}
/**
- * Checks that if left-hand-side is {@link VariableElement} with propagated type, then it
- * assigned value type is compatible with this propagated type.
+ * If left-hand-side is {@link VariableElement} with propagated type, then remember type before
+ * current "basic block" and set new type.
*/
- private void checkPropagatedTypeCompatible(DartExpression lhsNode, Type rhs) {
- Element element = lhsNode.getElement();
- if (ElementKind.of(element) == ElementKind.VARIABLE
- || ElementKind.of(element) == ElementKind.FIELD) {
- Type variableType = element.getType();
- if (variableType.isInferred() && !types.isAssignable(variableType, rhs)) {
- Elements.setType(element, dynamicType);
+ private void setVariableElementType(Element element, Type rhs) {
+ if (ElementKind.of(element) == ElementKind.VARIABLE) {
+ VariableElement variableElement = (VariableElement) element;
+ // remember type of this variable before "basic block"
+ Type typeBeforeBlock = blockOldTypes.getFirst().get(variableElement);
+ if (typeBeforeBlock == null) {
+ blockOldTypes.getFirst().put(variableElement, variableElement.getType());
}
+ // set new type
+ Type newType = Types.makeInferred(rhs);
+ Elements.setType(variableElement, newType);
}
}
@@ -856,6 +870,29 @@ public class TypeAnalyzer implements DartCompilationPhase {
}
}
+ private static Map<VariableElement, Type> getNewTypesAndRestoreOld(Map<VariableElement, Type> oldTypes) {
+ Map<VariableElement, Type> result = Maps.newHashMap();
+ for (Entry<VariableElement, Type> entry : oldTypes.entrySet()) {
+ VariableElement variable = entry.getKey();
+ result.put(variable, variable.getType());
+ Elements.setType(variable, entry.getValue());
+ }
+ return result;
+ }
+
+ /**
+ * When we cannot prove that node was visited, then type is intersection of old/new types.
+ */
+ private void setMergedVariableTypes(Map<VariableElement, Type> oldVariableTypes) {
+ for (Entry<VariableElement, Type> entry : oldVariableTypes.entrySet()) {
+ VariableElement variable = entry.getKey();
+ Type oldType = entry.getValue();
+ Type newType = variable.getType();
+ Type mergedType = types.intersection(newType, oldType);
+ setVariableElementType(variable, mergedType);
+ }
+ }
+
private boolean checkAssignable(DartNode node, Type t, Type s) {
t.getClass(); // Null check.
s.getClass(); // Null check.
@@ -1567,7 +1604,14 @@ public class TypeAnalyzer implements DartCompilationPhase {
if (elementType != null) {
variableRestorer.setType(variableElement, elementType);
}
- return typeAsVoid(node.getBody());
+ Map<VariableElement, Type> oldVariableTypes = Maps.newHashMap();
+ blockOldTypes.addFirst(oldVariableTypes);
+ try {
+ return typeAsVoid(node.getBody());
+ } finally {
+ blockOldTypes.removeFirst();
+ setMergedVariableTypes(oldVariableTypes);
+ }
} finally {
variableRestorer.restore();
}
@@ -1578,18 +1622,32 @@ public class TypeAnalyzer implements DartCompilationPhase {
typeOf(node.getInit());
DartExpression condition = node.getCondition();
checkCondition(condition);
- visitConditionalNode(condition, node.getBody());
- visitConditionalNode(condition, node.getIncrement());
+ // visit body
+ Map<VariableElement, Type> oldVariableTypes = Maps.newHashMap();
+ blockOldTypes.addFirst(oldVariableTypes);
+ try {
+ visitConditionalNode(condition, node.getBody());
+ visitConditionalNode(condition, node.getIncrement());
+ } finally {
+ blockOldTypes.removeFirst();
+ setMergedVariableTypes(oldVariableTypes);
+ }
+ // done
return voidType;
}
@Override
public Type visitFunction(DartFunction node) {
- Type previous = expected;
- visit(node.getParameters());
- expected = typeOf(node.getReturnTypeNode());
- typeOf(node.getBody());
- expected = previous;
+ blockOldTypes.addFirst(Maps.<VariableElement, Type> newHashMap());
+ try {
+ Type previous = expected;
+ visit(node.getParameters());
+ expected = typeOf(node.getReturnTypeNode());
+ typeOf(node.getBody());
+ expected = previous;
+ } finally {
+ blockOldTypes.removeFirst();
+ }
return voidType;
}
@@ -1687,23 +1745,56 @@ public class TypeAnalyzer implements DartCompilationPhase {
DartExpression condition = node.getCondition();
checkCondition(condition);
// visit "then"
+ Map<VariableElement, Type> thenOldVariableTypes = Maps.newHashMap();
+ blockOldTypes.addFirst(thenOldVariableTypes);
DartStatement thenStatement = node.getThenStatement();
visitConditionalNode(condition, thenStatement);
+ blockOldTypes.removeFirst();
+ Map<VariableElement, Type> thenVariableTypes = getNewTypesAndRestoreOld(thenOldVariableTypes);
// visit "else"
+ DartStatement elseStatement = node.getElseStatement();
+ Map<VariableElement, Type> elseOldVariableTypes = Maps.newHashMap();
{
- DartStatement elseStatement = node.getElseStatement();
VariableElementsRestorer variableRestorer = new VariableElementsRestorer();
// if has "else", then types inferred from "is! Type" applied only to "else"
if (elseStatement != null) {
+ blockOldTypes.addFirst(elseOldVariableTypes);
inferVariableTypesFromIsNotConditions(condition, variableRestorer);
typeOf(elseStatement);
variableRestorer.restore();
+ blockOldTypes.removeFirst();
}
// if no "else", then inferred types applied to the end of the method
if (elseStatement == null && isExitFromFunction(thenStatement)) {
inferVariableTypesFromIsNotConditions(condition, variableRestorer);
}
-
+ }
+ Map<VariableElement, Type> elseVariableTypes = getNewTypesAndRestoreOld(elseOldVariableTypes);
+ // merge variable types
+ {
+ Set<VariableElement> variables = Sets.newHashSet();
+ variables.addAll(thenVariableTypes.keySet());
+ variables.addAll(elseVariableTypes.keySet());
+ for (VariableElement variable : variables) {
+ List<Type> possibleTypes = Lists.newArrayList();
+ Type thenType = thenVariableTypes.get(variable);
+ Type elseType = elseVariableTypes.get(variable);
+ if (thenType != null && elseType != null) {
+ possibleTypes.add(thenType);
+ possibleTypes.add(elseType);
+ }
+ if (thenType != null && elseType == null) {
+ possibleTypes.add(thenType);
+ possibleTypes.add(variable.getType());
+ }
+ if (thenType == null && elseType != null) {
+ possibleTypes.add(variable.getType());
+ possibleTypes.add(elseType);
+ }
+ // do merge
+ Type mergedType = types.intersection(possibleTypes);
+ setVariableElementType(variable, mergedType);
+ }
}
// done
return voidType;
@@ -2339,7 +2430,11 @@ public class TypeAnalyzer implements DartCompilationPhase {
if (node.getParent() instanceof DartExprStmt
&& node.getParent().getParent() instanceof DartBlock) {
DartBlock restoreBlock = getBlockForAssertTypesInference(node);
- VariableElementsRestorer variableRestorer = new VariableElementsRestorer();
+ VariableElementsRestorer variableRestorer = restoreOnBlockExit.get(restoreBlock);
+ if (variableRestorer == null) {
+ variableRestorer = new VariableElementsRestorer();
+ restoreOnBlockExit.put(restoreBlock, variableRestorer);
+ }
restoreOnBlockExit.put(restoreBlock, variableRestorer);
inferVariableTypesFromIsConditions(condition, variableRestorer);
}
@@ -2385,7 +2480,7 @@ public class TypeAnalyzer implements DartCompilationPhase {
DartBlock block = (DartBlock) node;
DartNode p = block.getParent();
if (p instanceof DartIfStatement || p instanceof DartForStatement
- || p instanceof DartForInStatement || p instanceof DartDoWhileStatement) {
+ || p instanceof DartForInStatement || p instanceof DartWhileStatement) {
return block;
}
}
@@ -2520,7 +2615,16 @@ public class TypeAnalyzer implements DartCompilationPhase {
public Type visitWhileStatement(DartWhileStatement node) {
DartExpression condition = node.getCondition();
checkCondition(condition);
- visitConditionalNode(condition, node.getBody());
+ // visit body
+ Map<VariableElement, Type> oldVariableTypes = Maps.newHashMap();
+ blockOldTypes.addFirst(oldVariableTypes);
+ try {
+ visitConditionalNode(condition, node.getBody());
+ } finally {
+ blockOldTypes.removeFirst();
+ setMergedVariableTypes(oldVariableTypes);
+ }
+ // done
return voidType;
}
« no previous file with comments | « no previous file | compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698