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

Unified Diff: sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart

Issue 12327010: Infer types of final fields. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 10 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 | « sdk/lib/_internal/compiler/implementation/ssa/types.dart ('k') | tests/language/field_override2_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (revision 18819)
+++ sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (working copy)
@@ -35,6 +35,53 @@
bool get isEmpty => queue.isEmpty;
}
+/**
+ * Placeholder for type information of final fields of classes.
+ */
+class ClassInfoForFinalFields {
+ /**
+ * Maps a final field to a map from generative constructor to the
+ * inferred type of the field in that generative constructor.
+ */
+ final Map<Element, Map<Element, Element>> typesOfFinalFields =
+ new Map<Element, Map<Element, Element>>();
+
+ /**
+ * The number of generative constructors that need to be visited
+ * before we can take any decision on the type of the fields.
+ * Given that all generative constructors must be analyzed before
+ * re-analyzing one, we know that once [constructorsToVisitCount]
+ * reaches to 0, all generative constructors have been analyzed.
+ */
+ int constructorsToVisitCount;
+
+ ClassInfoForFinalFields(this.constructorsToVisitCount);
+
+ /**
+ * Records that the generative [constructor] has inferred [type]
+ * for the final [field].
+ */
+ void recordFinalFieldType(Element constructor, Element field, Element type) {
+ Map<Element, Element> typesFor = typesOfFinalFields.putIfAbsent(
+ field, () => new Map<Element, Element>());
+ typesFor[constructor] = type;
+ }
+
+ /**
+ * Records that [constructor] has been analyzed. If not at 0,
+ * decrement [constructorsToVisitCount].
+ */
+ void doneAnalyzingGenerativeConstructor(Element constructor) {
+ if (constructorsToVisitCount != 0) constructorsToVisitCount--;
+ }
+
+ /**
+ * Returns whether all generative constructors of the class have
+ * been analyzed.
+ */
+ bool get isDone => constructorsToVisitCount == 0;
+}
+
class SimpleTypesInferrer extends TypesInferrer {
/**
* Maps an element to its callers.
@@ -61,6 +108,13 @@
final Map<Element, int> analyzeCount = new Map<Element, int>();
/**
+ * Maps a class to a [ClassInfoForFinalFields] to help collect type
+ * information of final fields.
+ */
+ final Map<ClassElement, ClassInfoForFinalFields> classInfoForFinalFields =
+ new Map<ClassElement, ClassInfoForFinalFields>();
+
+ /**
* The work list of the inferrer.
*/
final WorkSet<Element> workSet = new WorkSet<Element>();
@@ -205,6 +259,21 @@
set.forEach((e) { workSet.add(e); });
}
}
+
+ // Build the [classInfoForFinalFields] map by iterating over all
+ // seen classes and counting the number of their generative
+ // constructors.
+ // We iterate over the seen classes and not the instantiated ones,
+ // because we also need to analyze the final fields of super
+ // classes that are not instantiated.
+ compiler.enqueuer.resolution.seenClasses.forEach((ClassElement cls) {
+ int constructorCount = 0;
+ cls.forEachMember((_, member) {
+ if (member.isGenerativeConstructor()) constructorCount++;
+ });
+ classInfoForFinalFields[cls.implementation] =
+ new ClassInfoForFinalFields(constructorCount);
+ });
}
dump() {
@@ -231,16 +300,33 @@
void clear() {
callersOf.clear();
analyzeCount.clear();
+ classInfoForFinalFields.clear();
}
bool analyze(Element element) {
- if (element.isField()) {
- // TODO(ngeoffray): Analyze its initializer.
+ SimpleTypeInferrerVisitor visitor =
+ new SimpleTypeInferrerVisitor(element, compiler, this);
+ Element returnType = visitor.run();
+ if (analyzeCount.containsKey(element)) {
+ analyzeCount[element]++;
+ } else {
+ analyzeCount[element] = 1;
+ }
+ if (element.isGenerativeConstructor()) {
+ // We always know the return type of a generative constructor.
return false;
+ } else if (element.isField()) {
+ if (element.modifiers.isFinal() || element.modifiers.isConst()) {
+ if (element.parseNode(compiler).asSendSet() != null) {
+ // If [element] is final and has an initializer, we record
+ // the inferred type.
+ return recordReturnType(element, returnType);
+ }
+ }
+ // We don't record anything for non-final fields.
+ return false;
} else {
- SimpleTypeInferrerVisitor visitor =
- new SimpleTypeInferrerVisitor(element, compiler, this);
- return visitor.run();
+ return recordReturnType(element, returnType);
}
}
@@ -252,30 +338,14 @@
bool recordReturnType(analyzedElement, returnType) {
assert(returnType != null);
Element existing = returnTypeOf[analyzedElement];
- if (existing == null) {
- // First time we analyzed [analyzedElement]. Initialize the
- // return type.
- assert(!analyzeCount.containsKey(analyzedElement));
- returnTypeOf[analyzedElement] = returnType;
- // If the return type is useful, say it has changed.
- return returnType != compiler.dynamicClass
- && returnType != compiler.nullClass;
- } else if (existing == compiler.dynamicClass) {
- // Previous analysis did not find any type.
- returnTypeOf[analyzedElement] = returnType;
- // If the return type is useful, say it has changed.
- return returnType != compiler.dynamicClass
- && returnType != compiler.nullClass;
- } else if (existing == giveUpType) {
- // If we already gave up on the return type, we don't change it.
- return false;
- } else if (existing != returnType) {
- // The method is returning two different types. Give up for now.
- // TODO(ngeoffray): Compute LUB.
- returnTypeOf[analyzedElement] = giveUpType;
- return true;
- }
- return false;
+ Element newType = existing == compiler.dynamicClass
+ ? returnType // Previous analysis did not find any type.
+ : computeLUB(existing, returnType);
+ returnTypeOf[analyzedElement] = newType;
+ // If the return type is useful, say it has changed.
+ return existing != newType
+ && newType != compiler.dynamicClass
+ && newType != compiler.nullClass;
}
/**
@@ -289,6 +359,7 @@
if (returnType == null || returnType == giveUpType) {
return compiler.dynamicClass;
}
+ assert(returnType != null);
return returnType;
}
@@ -317,6 +388,9 @@
return true;
}
});
+ if (result == null) {
+ result = compiler.dynamicClass;
+ }
return result;
}
@@ -329,8 +403,8 @@
ArgumentsTypes arguments) {
if (analyzeCount.containsKey(caller)) return;
callee = callee.implementation;
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- callee, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ callee, () => new Set<Element>());
callers.add(caller);
}
@@ -342,8 +416,8 @@
Element callee) {
if (analyzeCount.containsKey(caller)) return;
callee = callee.implementation;
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- callee, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ callee, () => new Set<Element>());
callers.add(caller);
}
@@ -357,8 +431,8 @@
if (analyzeCount.containsKey(caller)) return;
iterateOverElements(selector, (Element element) {
assert(element.isImplementation);
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- element, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ element, () => new Set<Element>());
callers.add(caller);
return true;
});
@@ -372,8 +446,8 @@
if (analyzeCount.containsKey(caller)) return;
iterateOverElements(selector, (Element element) {
assert(element.isImplementation);
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- element, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ element, () => new Set<Element>());
callers.add(caller);
return true;
});
@@ -430,6 +504,73 @@
}
}
}
+
+ /**
+ * Records in [classInfoForFinalFields] that [constructor] has
+ * inferred [type] for the final [field].
+ */
+ void recordFinalFieldType(Element constructor, Element field, Element type) {
+ // If the field is being set at its declaration site, it is not
+ // being tracked in the [classInfoForFinalFields] map.
+ if (constructor == field) return;
+ assert(field.modifiers.isFinal() || field.modifiers.isConst());
+ ClassElement cls = constructor.getEnclosingClass();
+ ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
+ info.recordFinalFieldType(constructor, field, type);
+ }
+
+ /**
+ * Records that we are done analyzing [constructor]. If all
+ * generative constructors of its enclosing class have already been
+ * analyzed, this method updates the types of final fields.
+ */
+ void doneAnalyzingGenerativeConstructor(Element constructor) {
+ ClassElement cls = constructor.getEnclosingClass();
+ ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
+ info.doneAnalyzingGenerativeConstructor(constructor);
+ if (info.isDone) {
+ updateFieldTypes(info);
+ }
+ }
+
+ /**
+ * Updates types of final fields listed in [info].
+ */
+ void updateFieldTypes(ClassInfoForFinalFields info) {
+ assert(info.isDone);
+ info.typesOfFinalFields.forEach((Element field,
+ Map<Element, Element> types) {
+ assert(field.modifiers.isFinal());
+ Element fieldType;
+ types.forEach((_, type) {
+ fieldType = computeLUB(fieldType, type);
+ });
+ returnTypeOf[field] = fieldType;
+ });
+ }
+
+ /**
+ * Returns the least upper bound between [firstType] and
+ * [secondType].
+ */
+ Element computeLUB(Element firstType, Element secondType) {
+ assert(secondType != null);
+ if (firstType == null) {
+ return secondType;
+ } else if (firstType == giveUpType) {
+ return firstType;
+ } else if (secondType == compiler.dynamicClass) {
+ return secondType;
+ } else if (firstType == compiler.dynamicClass) {
+ return firstType;
+ } else if (firstType != secondType) {
+ // TODO(ngeoffray): Actually compute the least upper bound.
+ return giveUpType;
+ } else {
+ assert(firstType == secondType);
+ return firstType;
+ }
+ }
}
/**
@@ -444,12 +585,12 @@
}
class SimpleTypeInferrerVisitor extends ResolvedVisitor {
- final FunctionElement analyzedElement;
+ final Element analyzedElement;
final SimpleTypesInferrer inferrer;
final Compiler compiler;
Element returnType;
- SimpleTypeInferrerVisitor(FunctionElement element,
+ SimpleTypeInferrerVisitor(Element element,
Compiler compiler,
this.inferrer)
: super(compiler.enqueuer.resolution.resolvedElements[element.declaration]),
@@ -458,49 +599,42 @@
assert(elements != null);
}
- bool run() {
- FunctionExpression node =
- analyzedElement.implementation.parseNode(compiler);
- bool changed;
- if (analyzedElement.isGenerativeConstructor()) {
- FunctionSignature signature = analyzedElement.computeSignature(compiler);
- // TODO(ngeoffray): handle initializing formals.
- // TODO(ngeoffray): handle initializers.
- node.body.accept(this);
- // We always know the return type of a generative constructor.
- changed = false;
+ Element run() {
+ var node = analyzedElement.implementation.parseNode(compiler);
+ if (analyzedElement.isField()) {
+ returnType = visit(node);
+ } else if (analyzedElement.isGenerativeConstructor()) {
+ FunctionElement function = analyzedElement;
+ FunctionSignature signature = function.computeSignature(compiler);
+ signature.forEachParameter((element) {
+ if (element.kind == ElementKind.FIELD_PARAMETER
+ && element.modifiers.isFinal()) {
+ // We don't track argument types yet, so just set the field
+ // as dynamic.
+ inferrer.recordFinalFieldType(
+ analyzedElement, element.fieldElement, compiler.dynamicClass);
+ }
+ });
+ visit(node.initializers);
+ visit(node.body);
+ inferrer.doneAnalyzingGenerativeConstructor(analyzedElement);
+ returnType = analyzedElement.getEnclosingClass();
} else if (analyzedElement.isNative()) {
// Native methods do not have a body, and we currently just say
// they return dynamic.
- inferrer.recordReturnType(analyzedElement, compiler.dynamicClass);
- changed = false;
+ returnType = compiler.dynamicClass;
} else {
- node.body.accept(this);
+ visit(node.body);
if (returnType == null) {
// No return in the body.
returnType = compiler.nullClass;
}
- changed = inferrer.recordReturnType(analyzedElement, returnType);
}
- if (inferrer.analyzeCount.containsKey(analyzedElement)) {
- inferrer.analyzeCount[analyzedElement]++;
- } else {
- inferrer.analyzeCount[analyzedElement] = 1;
- }
- return changed;
+ return returnType;
}
recordReturnType(ClassElement cls) {
- if (returnType == null) {
- returnType = cls;
- } else if (returnType != inferrer.giveUpType
- && cls == compiler.dynamicClass) {
- returnType = cls;
- } else if (returnType == compiler.dynamicClass) {
- // Nothing to do. Stay dynamic.
- } else if (leastUpperBound(cls, returnType) == compiler.dynamicClass) {
- returnType = inferrer.giveUpType;
- }
+ returnType = inferrer.computeLUB(returnType, cls);
}
visitNode(Node node) {
@@ -512,6 +646,10 @@
return node.send.accept(this);
}
+ visit(Node node) {
+ return node == null ? compiler.dynamicClass : node.accept(this);
+ }
+
visitFunctionExpression(FunctionExpression node) {
// We don't put the closure in the work queue of the
// inferrer, because it will share information with its enclosing
@@ -563,9 +701,52 @@
}
visitSendSet(SendSet node) {
- // TODO(ngeoffray): return the right hand side's type.
- node.visitChildren(this);
- return compiler.dynamicClass;
+ Element element = elements[node];
+ if (!Elements.isUnresolved(element) && element.impliesType()) {
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
+
+ Operator op = node.assignmentOperator;
+ if (node.isSuperCall) {
+ // [: super.foo = 42 :] or [: super.foo++ :] or [: super.foo += 1 :].
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ } else if (node.isIndex) {
+ if (const SourceString("=") == op.source) {
+ // [: foo[0] = 42 :]
+ visit(node.receiver);
+ Element returnType;
+ for (Node argument in node.arguments) {
+ returnType = argument.accept(this);
+ }
+ return returnType;
+ } else {
+ // [: foo[0] += 42 :] or [: foo[0]++ :].
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
+ } else if (const SourceString("=") == op.source) {
+ // [: foo = 42 :]
+ visit(node.receiver);
+ Link<Node> link = node.arguments;
+ assert(!link.isEmpty && link.tail.isEmpty);
+ Element type = link.head.accept(this);
+
+ if (!Elements.isUnresolved(element)
+ && element.isField()
+ && element.modifiers.isFinal()) {
+ inferrer.recordFinalFieldType(analyzedElement, element, type);
+ }
+ return type;
+ } else {
+ // [: foo++ :] or [: foo += 1 :].
+ assert(const SourceString("++") == op.source ||
+ const SourceString("--") == op.source ||
+ node.assignmentOperator.source.stringValue.endsWith("="));
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
}
visitIdentifier(Identifier node) {
@@ -712,14 +893,8 @@
visitGetterSend(Send node) {
Element element = elements[node];
if (Elements.isStaticOrTopLevelField(element)) {
- if (element.isGetter()) {
- inferrer.registerGetterOnElement(analyzedElement, element);
- return inferrer.returnTypeOfElement(element);
- } else {
- // Nothing yet.
- // TODO: Analyze initializer of element.
- return compiler.dynamicClass;
- }
+ inferrer.registerGetterOnElement(analyzedElement, element);
+ return inferrer.returnTypeOfElement(element);
} else if (Elements.isInstanceSend(node, elements)) {
ClassElement receiverType;
if (node.receiver == null) {
@@ -768,14 +943,11 @@
node.condition.accept(this);
Element firstType = node.thenExpression.accept(this);
Element secondType = node.elseExpression.accept(this);
- return leastUpperBound(firstType, secondType);
+ Element type = inferrer.computeLUB(firstType, secondType);
+ if (type == inferrer.giveUpType) type = compiler.dynamicClass;
+ return type;
}
- leastUpperBound(Element firstType, Element secondType) {
- if (firstType == secondType) return firstType;
- return compiler.dynamicClass;
- }
-
internalError(String reason, {Node node}) {
compiler.internalError(reason, node: node);
}
« no previous file with comments | « sdk/lib/_internal/compiler/implementation/ssa/types.dart ('k') | tests/language/field_override2_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698