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