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

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

Issue 12811010: - Introduce the notion of setter constraints in the type inferrer, where we record things like: fie… (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 9 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
Index: sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (revision 20142)
+++ sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (working copy)
@@ -4,8 +4,10 @@
library simple_types_inferrer;
+import 'dart:collection' show Queue;
+
import '../closure.dart' show ClosureClassMap, ClosureScope;
-import '../dart_types.dart' show DartType, FunctionType;
+import '../dart_types.dart' show DartType, FunctionType, TypeKind;
import '../elements/elements.dart';
import '../native_handler.dart' as native;
import '../tree/tree.dart';
@@ -20,21 +22,21 @@
/**
* A work queue that ensures there are no duplicates, and adds and
- * removes in LIFO.
+ * removes in FIFO.
*/
class WorkSet<E extends Element> {
- final List<E> queue = new List<E>();
+ final Queue<E> queue = new Queue<E>();
final Set<E> elementsInQueue = new Set<E>();
void add(E element) {
element = element.implementation;
if (elementsInQueue.contains(element)) return;
- queue.add(element);
+ queue.addLast(element);
elementsInQueue.add(element);
}
E remove() {
- E element = queue.removeLast();
+ E element = queue.removeFirst();
elementsInQueue.remove(element);
return element;
}
@@ -52,8 +54,8 @@
* 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, TypeMask>> typesOfFinalFields =
- new Map<Element, Map<Element, TypeMask>>();
+ final Map<Element, Map<Node, TypeMask>> typesOfFinalFields =
+ new Map<Element, Map<Node, TypeMask>>();
/**
* The number of generative constructors that need to be visited
@@ -70,10 +72,13 @@
* Records that the generative [constructor] has inferred [type]
* for the final [field].
*/
- void recordFinalFieldType(Element constructor, Element field, TypeMask type) {
- Map<Element, TypeMask> typesFor = typesOfFinalFields.putIfAbsent(
- field, () => new Map<Element, TypeMask>());
- typesFor[constructor] = type;
+ void recordFinalFieldType(Node node,
+ Element constructor,
+ Element field,
+ TypeMask type) {
+ Map<Node, TypeMask> typesFor = typesOfFinalFields.putIfAbsent(
+ field, () => new Map<Node, TypeMask>());
+ typesFor[node] = type;
}
/**
@@ -150,6 +155,23 @@
new Map<ClassElement, ClassInfoForFinalFields>();
/**
+ * A map of constraints on a setter. When computing the type
+ * of a field, these [Node] are initially discarded, and once the
+ * type is computed, we make sure these constraints are satisfied
+ * for that type. For example:
+ *
+ * [: field++ ], or [: field += 42 :], the constraint is on the
+ * operator+, and we make sure that a typed selector with the found
+ * type returns that type.
+ *
+ * [: field = other.field :], the constraint in on the [:field]
+ * getter selector, and we make sure that the getter selector
+ * returns that type.
+ *
+ */
+ final Map<Node, Selector> setterConstraints = new Map<Node, Selector>();
+
+ /**
* The work list of the inferrer.
*/
final WorkSet<Element> workSet = new WorkSet<Element>();
@@ -301,8 +323,8 @@
* Enqueues [e] in the work queue if it is valuable.
*/
void enqueueAgain(Element e) {
- TypeMask type = e.isField() ? typeOf[e] : returnTypeOf[e];
- if (analyzeCount[e] > MAX_ANALYSIS_COUNT_PER_ELEMENT) return;
+ int count = analyzeCount[e];
+ if (count != null && count > MAX_ANALYSIS_COUNT_PER_ELEMENT) return;
workSet.add(e);
}
@@ -335,8 +357,8 @@
set.add(element);
});
- // This iteration assumes the [WorkSet] is LIFO.
- for (int i = max; i >= 0; i--) {
+ // This iteration assumes the [WorkSet] is FIFO.
+ for (int i = 0; i <= max; i++) {
Set<Element> set = methodSizes[i];
if (set != null) {
set.forEach((e) { workSet.add(e); });
@@ -438,6 +460,7 @@
analyzeCount.clear();
classInfoForFinalFields.clear();
typeOfFields.clear();
+ setterConstraints.clear();
}
bool analyze(Element element) {
@@ -480,6 +503,7 @@
}
bool recordType(Element analyzedElement, TypeMask type) {
+ assert(type != null);
return internalRecordType(analyzedElement, type, typeOf);
}
@@ -527,9 +551,11 @@
});
} else if (element.isNative()) {
return returnTypeOf.putIfAbsent(element, () {
- // Only functions are native.
- FunctionType functionType = element.computeType(compiler);
- DartType returnType = functionType.returnType;
+ var elementType = element.computeType(compiler);
+ if (elementType.kind != TypeKind.FUNCTION) {
+ return dynamicType;
+ }
+ DartType returnType = elementType.returnType;
return returnType.isVoid
? nullType
: new TypeMask.subtype(returnType.asRaw());
@@ -550,9 +576,11 @@
TypeMask typeOfElement(Element element) {
element = element.implementation;
if (isNativeElement(element) && element.isField()) {
- return typeOf.putIfAbsent(element, () {
+ var type = typeOf.putIfAbsent(element, () {
return new TypeMask.subtype(element.computeType(compiler).asRaw());
});
+ assert(type != null);
+ return type;
}
TypeMask type = typeOf[element];
if (type == null || isGiveUpType(type)) {
@@ -579,25 +607,19 @@
if (element.isFunction()) {
type = functionType;
} else if (element.isField()) {
- type = typeOf[element];
- } else if (element.isGetter()) {
- type = returnTypeOf[element];
+ type = typeOfElement(element);
+ } else {
+ assert(element.isGetter());
+ type = returnTypeOfElement(element);
}
} else {
- type = returnTypeOf[element];
+ type = returnTypeOfElement(element);
}
- if (type == null
- || isDynamicType(type)
- || isGiveUpType(type)
- || (type != result && result != null)) {
- result = dynamicType;
- return false;
- } else {
- result = type;
- return true;
- }
+ result = computeLUB(result, type);
+ // Continue if the type is valuable.
+ return !isDynamicType(result) && !isGiveUpType(result);
});
- if (result == null) {
+ if (result == null || isGiveUpType(result)) {
result = dynamicType;
}
return result;
@@ -696,21 +718,69 @@
map[node] = argumentType;
// If we have analyzed all elements, we can update the type of the
// field right away.
- if (hasAnalyzedAll) updateNonFinalFieldType(element);
+ if (hasAnalyzedAll) {
+ // Only update if the new type provides value.
+ if (typeOf[element] != argumentType) {
+ updateNonFinalFieldType(element);
+ }
+ }
}
+ TypeMask computeFieldTypeWithConstraints(Element element, Map types) {
+ Set<Selector> constraints = new Set<Selector>();
+ TypeMask fieldType;
+ types.forEach((Node node, TypeMask mask) {
+ Selector constraint = setterConstraints[node];
+ if (constraint != null) {
+ // If this update has a constraint, we collect it and don't
+ // use its type.
+ constraints.add(constraint);
+ } else {
+ fieldType = computeLUB(fieldType, mask);
+ }
+ });
+
+ if (!constraints.isEmpty
+ && !isDynamicType(fieldType)
+ && !isGiveUpType(fieldType)) {
+ // Now that we have found a type, we go over the collected
+ // constraints, and make sure they apply to the found type. We
+ // update [typeOf] to make sure [typeOfSelector] knows the field
+ // type.
+ TypeMask existing = typeOf[element];
+ typeOf[element] = fieldType;
+
+ for (Selector constraint in constraints) {
+ if (constraint.isOperator()) {
+ // If the constraint is on an operator, we type the receiver
+ // to be the field.
+ constraint = new TypedSelector(fieldType, constraint);
+ } else {
+ // Otherwise the constraint is on the form [: field = other.field :].
+ assert(constraint.isGetter());
+ }
+ fieldType = computeLUB(fieldType, typeOfSelector(constraint));
+ }
+ if (existing == null) {
+ typeOf.remove(element);
+ } else {
+ typeOf[element] = existing;
+ }
+ }
+ return fieldType;
+ }
+
/**
* Computes the type of [element], based on all assignments we have
* collected on that [element]. This method can only be called after
* we have analyzed all elements in the world.
*/
void updateNonFinalFieldType(Element element) {
+ if (isNativeElement(element)) return;
assert(hasAnalyzedAll);
- TypeMask fieldType;
- typeOfFields[element].forEach((_, TypeMask mask) {
- fieldType = computeLUB(fieldType, mask);
- });
+ TypeMask fieldType = computeFieldTypeWithConstraints(
+ element, typeOfFields[element]);
// If the type of [element] has changed, re-analyze its users.
if (recordType(element, fieldType)) {
@@ -722,14 +792,15 @@
* Records in [classInfoForFinalFields] that [constructor] has
* inferred [type] for the final [field].
*/
- void recordFinalFieldType(Element constructor, Element field, TypeMask type) {
+ void recordFinalFieldType(
+ Node node, Element constructor, Element field, TypeMask 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);
+ info.recordFinalFieldType(node, constructor, field, type);
}
/**
@@ -752,13 +823,13 @@
void updateFinalFieldsType(ClassInfoForFinalFields info) {
assert(info.isDone);
info.typesOfFinalFields.forEach((Element field,
- Map<Element, TypeMask> types) {
+ Map<Node, TypeMask> types) {
+ if (isNativeElement(field)) return;
assert(field.modifiers.isFinal());
- TypeMask fieldType;
- types.forEach((_, TypeMask type) {
- fieldType = computeLUB(fieldType, type);
- });
- recordType(field, fieldType);
+ TypeMask fieldType = computeFieldTypeWithConstraints(field, types);
+ if (recordType(field, fieldType)) {
+ enqueueCallersOf(field);
+ }
});
}
@@ -785,6 +856,10 @@
return union.containsAll(compiler) ? giveUpType : union;
}
}
+
+ void recordSetterConstraint(Node node, Selector selector) {
+ setterConstraints[node] = selector;
+ }
}
/**
@@ -995,7 +1070,10 @@
if (element.kind == ElementKind.FIELD_PARAMETER) {
if (element.fieldElement.modifiers.isFinal()) {
inferrer.recordFinalFieldType(
- analyzedElement, element.fieldElement, inferrer.dynamicType);
+ node,
+ analyzedElement,
+ element.fieldElement,
+ inferrer.dynamicType);
} else {
inferrer.recordNonFinalFieldElementType(
element.parseNode(compiler),
@@ -1312,7 +1390,8 @@
handleDynamicSend(node, setterSelector, receiverType, arguments);
} else if (element.isField()) {
if (element.modifiers.isFinal()) {
- inferrer.recordFinalFieldType(outermostElement, element, rhsType);
+ inferrer.recordFinalFieldType(
+ node, outermostElement, element, rhsType);
} else {
locals.updateField(element, rhsType);
if (visitingInitializers) {
@@ -1324,6 +1403,21 @@
} else if (Elements.isLocal(element)) {
locals.update(element, rhsType);
}
+
+ if (!Elements.isLocal(element)) {
+ // Recognize a constraint of the form [: field = other.field :]
+ // or [: field = field + 42 :].
+ var rhs = node.arguments.head;
+ if (rhs.asSend() != null
+ && rhs.isPropertyAccess
+ && rhs.selector.source == node.selector.source) {
+ // TODO(ngeoffray): We should update selectors in the
+ // element tree and find out if the typed selector still
+ // applies to the receiver type.
+ Selector constraint = elements.getSelector(rhs);
+ inferrer.recordSetterConstraint(node, constraint);
+ }
+ }
return rhsType;
} else {
// [: foo++ :] or [: foo += 1 :].
@@ -1373,6 +1467,11 @@
node, operatorSelector, getterType, operatorArguments);
}
+ if (!Elements.isLocal(element)) {
+ // Record a constraint of the form [: field++ :], or [: field += 42 :].
+ inferrer.recordSetterConstraint(node, operatorSelector);
+ }
+
if (node.isPostfix) {
return getterType;
} else {
« no previous file with comments | « sdk/lib/_internal/compiler/implementation/ssa/types.dart ('k') | tests/compiler/dart2js/field_type_simple_inferer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698