Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/typechecker.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart |
| index 2cc56efd9b0ea3a2ece6b4efb2e69d2d67192d89..700975111dc19fe28ed3876acadde82dd9265889 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/typechecker.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/typechecker.dart |
| @@ -113,6 +113,21 @@ class ResolvedAccess extends ElementAccess { |
| String toString() => 'ResolvedAccess($element)'; |
| } |
| +/// An access to a promoted variable. |
| +class PromotedAccess extends ElementAccess { |
| + final VariableElement element; |
| + final DartType type; |
| + |
| + PromotedAccess(VariableElement this.element, DartType this.type) { |
| + assert(element != null); |
| + assert(type != null); |
| + } |
| + |
| + DartType computeType(Compiler compiler) => type; |
| + |
| + String toString() => 'PromotedAccess($element,$type)'; |
| +} |
| + |
| /** |
| * An access of a resolved top-level or static property or function, or an |
| * access of a resolved element through [:this:]. |
| @@ -167,6 +182,46 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| DartType objectType; |
| DartType listType; |
| + Map<Node, Map<VariableElement, DartType>> shownTypesMap = |
| + new Map<Node, Map<VariableElement, DartType>>(); |
| + |
| + Map<VariableElement, Link<DartType>> knownTypesMap = |
| + new Map<VariableElement, Link<DartType>>(); |
| + |
| + void showType(Node node, VariableElement element, DartType type) { |
| + Map<VariableElement, DartType> shownTypes = shownTypesMap.putIfAbsent(node, |
| + () => new Map<VariableElement, DartType>()); |
| + shownTypes[element] = type; |
| + } |
| + |
| + void registerKnownType(VariableElement element, DartType type) { |
| + Link<DartType> knownTypes = |
| + knownTypesMap.putIfAbsent(element, () => const Link<DartType>()); |
| + knownTypesMap[element] = knownTypes.prepend(type); |
| + } |
| + |
| + void unregisterKnownType(VariableElement element) { |
| + Link<DartType> knownTypes = knownTypesMap[element].tail; |
| + if (knownTypes.isEmpty) { |
| + knownTypesMap.remove(element); |
| + } else { |
| + knownTypesMap[element] = knownTypes; |
| + } |
| + } |
| + |
| + Map<VariableElement, DartType> getShownTypesFor(Node node) { |
| + Map<VariableElement, DartType> shownTypes = shownTypesMap[node]; |
| + return shownTypes != null ? shownTypes : const {}; |
| + } |
| + |
| + DartType getKnownType(VariableElement element) { |
| + Link<DartType> promotions = knownTypesMap[element]; |
| + if (promotions != null) { |
| + return promotions.head; |
| + } |
| + return element.computeType(compiler); |
| + } |
| + |
| TypeCheckerVisitor(this.compiler, TreeElements elements, this.types) |
| : this.elements = elements, |
| currentClass = elements.currentElement != null |
| @@ -230,6 +285,33 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| return result; |
| } |
| + /// Analyze [node] in the context of the known types shown in [context]. |
| + DartType analyzeInPromotedContext(Node context, Node node) { |
| + Link<VariableElement> knownForArgument = const Link<VariableElement>(); |
| + Map<VariableElement, DartType> shownForReceiver = |
| + getShownTypesFor(context); |
| + shownForReceiver.forEach((VariableElement variable, DartType type) { |
| + if (elements.isPotentiallyMutatedIn(context, variable)) return; |
| + if (elements.isPotentiallyMutatedIn(node, variable)) return; |
| + if (elements.isPotentiallyMutatedInClosure(variable)) return; |
| + if (elements.isAccessedByClosureIn(node, variable) && |
| + elements.isPotentiallyMutated(variable)) { |
| + return; |
| + } |
| + knownForArgument = knownForArgument.prepend(variable); |
| + registerKnownType(variable, type); |
| + }); |
| + |
| + final DartType type = analyze(node); |
| + |
| + while (!knownForArgument.isEmpty) { |
| + unregisterKnownType(knownForArgument.head); |
| + knownForArgument = knownForArgument.tail; |
| + } |
| + |
| + return type; |
| + } |
| + |
| /** |
| * Check if a value of type t can be assigned to a variable, |
| * parameter or return value of type s. |
| @@ -371,8 +453,13 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| } |
| DartType visitIf(If node) { |
| + Expression condition = node.condition.expression; |
| + Statement thenPart = node.thenPart; |
| + |
| checkCondition(node.condition); |
| - StatementType thenType = analyze(node.thenPart); |
| + |
| + StatementType thenType = analyzeInPromotedContext(condition, thenPart); |
| + |
| StatementType elseType = node.hasElsePart ? analyze(node.elsePart) |
| : StatementType.NOT_RETURNING; |
| return thenType.join(elseType); |
| @@ -538,6 +625,7 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| analyzeArguments(node, elementAccess.element, types.dynamicType, |
| argumentTypes); |
| } |
| + type = type.unalias(compiler); |
| if (identical(type.kind, TypeKind.FUNCTION)) { |
| FunctionType funType = type; |
| return funType.returnType; |
| @@ -621,6 +709,16 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| ElementAccess createResolvedAccess(Send node, SourceString name, |
| Element element) { |
| checkPrivateAccess(node, element, name); |
| + return createPromotedAccess(element); |
| + } |
| + |
| + ElementAccess createPromotedAccess(Element element) { |
| + if (element.isVariable() || element.isParameter()) { |
| + Link<DartType> knownTypes = knownTypesMap[element]; |
| + if (knownTypes != null) { |
| + return new PromotedAccess(element, knownTypes.head); |
| + } |
| + } |
| return new ResolvedAccess(element); |
| } |
| @@ -660,7 +758,7 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| if (Elements.isClosureSend(node, element)) { |
| if (element != null) { |
| // foo() where foo is a local or a parameter. |
| - return analyzeInvocation(node, new ResolvedAccess(element)); |
| + return analyzeInvocation(node, createPromotedAccess(element)); |
| } else { |
| // exp() where exp is some complex expression like (o) or foo(). |
| DartType type = analyze(node.selector); |
| @@ -673,6 +771,20 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| if (node.isOperator && identical(name, 'is')) { |
| analyze(node.receiver); |
| + if (!node.isIsNotCheck) { |
| + Element variable = elements[node.receiver]; |
| + if (variable != null) { |
|
karlklose
2013/10/15 11:56:17
Merge the ifs.
Johnni Winther
2013/10/15 15:57:00
Done.
|
| + if (variable.isVariable() || variable.isParameter()) { |
| + DartType knownType = getKnownType(variable); |
| + if (!knownType.isDynamic) { |
| + DartType isType = elements.getType(node.arguments.head); |
| + if (types.isMoreSpecific(isType, knownType)) { |
| + showType(node, variable, isType); |
| + } |
| + } |
| + } |
| + } |
| + } |
| return boolType; |
| } if (node.isOperator && identical(name, 'as')) { |
| analyze(node.receiver); |
| @@ -686,13 +798,39 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| // Analyze argument. |
| analyze(node.arguments.head); |
| return boolType; |
| - } else if (identical(name, '||') || |
| - identical(name, '&&')) { |
| + } else if (identical(name, '||')) { |
| checkAssignable(receiver, receiverType, boolType); |
| final Node argument = node.arguments.head; |
| final DartType argumentType = analyze(argument); |
| checkAssignable(argument, argumentType, boolType); |
| return boolType; |
| + } else if (identical(name, '&&')) { |
| + checkAssignable(receiver, receiverType, boolType); |
| + final Node argument = node.arguments.head; |
| + |
| + final DartType argumentType = |
| + analyzeInPromotedContext(receiver, argument); |
| + |
| + Map<VariableElement, DartType> shownForReceiver = |
|
karlklose
2013/10/15 11:56:17
Share code with l. 823-830.
Johnni Winther
2013/10/15 15:57:00
Done.
|
| + getShownTypesFor(receiver); |
| + shownForReceiver.forEach((VariableElement variable, DartType type) { |
| + if (elements.isPotentiallyMutatedIn(receiver, variable)) return; |
| + if (elements.isPotentiallyMutatedIn(argument, variable)) return; |
| + if (elements.isPotentiallyMutatedInClosure(variable)) return; |
| + showType(node, variable, type); |
| + }); |
| + |
| + Map<VariableElement, DartType> shownForArgument = |
| + getShownTypesFor(argument); |
| + shownForArgument.forEach((VariableElement variable, DartType type) { |
| + if (elements.isPotentiallyMutatedIn(receiver, variable)) return; |
| + if (elements.isPotentiallyMutatedIn(argument, variable)) return; |
| + if (elements.isPotentiallyMutatedInClosure(variable)) return; |
| + showType(node, variable, type); |
| + }); |
| + |
| + checkAssignable(argument, argumentType, boolType); |
| + return boolType; |
| } else if (identical(name, '!')) { |
| checkAssignable(receiver, receiverType, boolType); |
| return boolType; |
| @@ -1124,8 +1262,13 @@ class TypeCheckerVisitor extends Visitor<DartType> { |
| } |
| DartType visitConditional(Conditional node) { |
| - checkCondition(node.condition); |
| - DartType thenType = analyzeNonVoid(node.thenExpression); |
| + Expression condition = node.condition; |
| + Expression thenExpression = node.thenExpression; |
| + |
| + checkCondition(condition); |
| + |
| + DartType thenType = analyzeInPromotedContext(condition, thenExpression); |
| + |
| DartType elseType = analyzeNonVoid(node.elseExpression); |
| if (types.isSubtype(thenType, elseType)) { |
| return thenType; |