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 aa24ea04a001e93e7c04b384164110ba082d59e6..8545eaadd48657314f7c3b8f58c3a8d318889b2f 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,32 @@ 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(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 +452,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 +624,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 +708,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 +757,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 +770,19 @@ 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 && |
+ (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 +796,30 @@ 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); |
+ |
+ void reshowTypes(VariableElement variable, DartType type) { |
+ if (elements.isPotentiallyMutatedIn(argument, variable)) return; |
+ if (elements.isPotentiallyMutatedInClosure(variable)) return; |
+ showType(node, variable, type); |
+ } |
+ |
+ getShownTypesFor(receiver).forEach(reshowTypes); |
+ getShownTypesFor(argument).forEach(reshowTypes); |
+ |
+ checkAssignable(argument, argumentType, boolType); |
+ return boolType; |
} else if (identical(name, '!')) { |
checkAssignable(receiver, receiverType, boolType); |
return boolType; |
@@ -1124,8 +1251,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; |