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

Unified Diff: pkg/compiler/lib/src/inferrer/type_system.dart

Issue 2616103002: Flatten inferrer implementation (Closed)
Patch Set: Updated cf. comment. Created 3 years, 11 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 | « pkg/compiler/lib/src/inferrer/type_graph_nodes.dart ('k') | pkg/compiler/lib/src/types/types.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/inferrer/type_system.dart
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
new file mode 100644
index 0000000000000000000000000000000000000000..da755b91a93462bea061c6881e6cbf0a934525aa
--- /dev/null
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -0,0 +1,555 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../common.dart';
+import '../elements/elements.dart';
+import '../elements/resolution_types.dart' show ResolutionDartType;
+import '../tree/dartstring.dart';
+import '../tree/nodes.dart' as ast;
+import '../types/masks.dart';
+import '../universe/selector.dart';
+import '../world.dart';
+import 'type_graph_nodes.dart';
+
+/**
+ * The class [SimpleInferrerVisitor] will use when working on types.
+ */
+class TypeSystem {
+ final ClosedWorld closedWorld;
+
+ /// [ElementTypeInformation]s for elements.
+ final Map<Element, TypeInformation> typeInformations =
+ new Map<Element, TypeInformation>();
+
+ /// [ListTypeInformation] for allocated lists.
+ final Map<ast.Node, TypeInformation> allocatedLists =
+ new Map<ast.Node, TypeInformation>();
+
+ /// [MapTypeInformation] for allocated Maps.
+ final Map<ast.Node, TypeInformation> allocatedMaps =
+ new Map<ast.Node, TypeInformation>();
+
+ /// Closures found during the analysis.
+ final Set<TypeInformation> allocatedClosures = new Set<TypeInformation>();
+
+ /// Cache of [ConcreteTypeInformation].
+ final Map<TypeMask, TypeInformation> concreteTypes =
+ new Map<TypeMask, TypeInformation>();
+
+ /// List of [TypeInformation]s allocated inside method bodies (calls,
+ /// narrowing, phis, and containers).
+ final List<TypeInformation> allocatedTypes = <TypeInformation>[];
+
+ Iterable<TypeInformation> get allTypes => [
+ typeInformations.values,
+ allocatedLists.values,
+ allocatedMaps.values,
+ allocatedClosures,
+ concreteTypes.values,
+ allocatedTypes
+ ].expand((x) => x);
+
+ TypeSystem(this.closedWorld) {
+ nonNullEmptyType = getConcreteTypeFor(commonMasks.emptyType);
+ }
+
+ CommonMasks get commonMasks => closedWorld.commonMasks;
+
+ /// Used to group [TypeInformation] nodes by the element that triggered their
+ /// creation.
+ MemberTypeInformation _currentMember = null;
+ MemberTypeInformation get currentMember => _currentMember;
+
+ void withMember(MemberElement element, action) {
+ assert(invariant(element, _currentMember == null,
+ message: "Already constructing graph for $_currentMember."));
+ _currentMember = getInferredTypeOf(element);
+ action();
+ _currentMember = null;
+ }
+
+ TypeInformation nullTypeCache;
+ TypeInformation get nullType {
+ if (nullTypeCache != null) return nullTypeCache;
+ return nullTypeCache = getConcreteTypeFor(commonMasks.nullType);
+ }
+
+ TypeInformation intTypeCache;
+ TypeInformation get intType {
+ if (intTypeCache != null) return intTypeCache;
+ return intTypeCache = getConcreteTypeFor(commonMasks.intType);
+ }
+
+ TypeInformation uint32TypeCache;
+ TypeInformation get uint32Type {
+ if (uint32TypeCache != null) return uint32TypeCache;
+ return uint32TypeCache = getConcreteTypeFor(commonMasks.uint32Type);
+ }
+
+ TypeInformation uint31TypeCache;
+ TypeInformation get uint31Type {
+ if (uint31TypeCache != null) return uint31TypeCache;
+ return uint31TypeCache = getConcreteTypeFor(commonMasks.uint31Type);
+ }
+
+ TypeInformation positiveIntTypeCache;
+ TypeInformation get positiveIntType {
+ if (positiveIntTypeCache != null) return positiveIntTypeCache;
+ return positiveIntTypeCache =
+ getConcreteTypeFor(commonMasks.positiveIntType);
+ }
+
+ TypeInformation doubleTypeCache;
+ TypeInformation get doubleType {
+ if (doubleTypeCache != null) return doubleTypeCache;
+ return doubleTypeCache = getConcreteTypeFor(commonMasks.doubleType);
+ }
+
+ TypeInformation numTypeCache;
+ TypeInformation get numType {
+ if (numTypeCache != null) return numTypeCache;
+ return numTypeCache = getConcreteTypeFor(commonMasks.numType);
+ }
+
+ TypeInformation boolTypeCache;
+ TypeInformation get boolType {
+ if (boolTypeCache != null) return boolTypeCache;
+ return boolTypeCache = getConcreteTypeFor(commonMasks.boolType);
+ }
+
+ TypeInformation functionTypeCache;
+ TypeInformation get functionType {
+ if (functionTypeCache != null) return functionTypeCache;
+ return functionTypeCache = getConcreteTypeFor(commonMasks.functionType);
+ }
+
+ TypeInformation listTypeCache;
+ TypeInformation get listType {
+ if (listTypeCache != null) return listTypeCache;
+ return listTypeCache = getConcreteTypeFor(commonMasks.listType);
+ }
+
+ TypeInformation constListTypeCache;
+ TypeInformation get constListType {
+ if (constListTypeCache != null) return constListTypeCache;
+ return constListTypeCache = getConcreteTypeFor(commonMasks.constListType);
+ }
+
+ TypeInformation fixedListTypeCache;
+ TypeInformation get fixedListType {
+ if (fixedListTypeCache != null) return fixedListTypeCache;
+ return fixedListTypeCache = getConcreteTypeFor(commonMasks.fixedListType);
+ }
+
+ TypeInformation growableListTypeCache;
+ TypeInformation get growableListType {
+ if (growableListTypeCache != null) return growableListTypeCache;
+ return growableListTypeCache =
+ getConcreteTypeFor(commonMasks.growableListType);
+ }
+
+ TypeInformation mapTypeCache;
+ TypeInformation get mapType {
+ if (mapTypeCache != null) return mapTypeCache;
+ return mapTypeCache = getConcreteTypeFor(commonMasks.mapType);
+ }
+
+ TypeInformation constMapTypeCache;
+ TypeInformation get constMapType {
+ if (constMapTypeCache != null) return constMapTypeCache;
+ return constMapTypeCache = getConcreteTypeFor(commonMasks.constMapType);
+ }
+
+ TypeInformation stringTypeCache;
+ TypeInformation get stringType {
+ if (stringTypeCache != null) return stringTypeCache;
+ return stringTypeCache = getConcreteTypeFor(commonMasks.stringType);
+ }
+
+ TypeInformation typeTypeCache;
+ TypeInformation get typeType {
+ if (typeTypeCache != null) return typeTypeCache;
+ return typeTypeCache = getConcreteTypeFor(commonMasks.typeType);
+ }
+
+ TypeInformation dynamicTypeCache;
+ TypeInformation get dynamicType {
+ if (dynamicTypeCache != null) return dynamicTypeCache;
+ return dynamicTypeCache = getConcreteTypeFor(commonMasks.dynamicType);
+ }
+
+ TypeInformation asyncFutureTypeCache;
+ // Subtype of Future returned by async methods.
+ TypeInformation get asyncFutureType {
+ if (asyncFutureTypeCache != null) return asyncFutureTypeCache;
+ return asyncFutureTypeCache =
+ getConcreteTypeFor(commonMasks.asyncFutureType);
+ }
+
+ TypeInformation syncStarIterableTypeCache;
+ TypeInformation get syncStarIterableType {
+ if (syncStarIterableTypeCache != null) return syncStarIterableTypeCache;
+ return syncStarIterableTypeCache =
+ getConcreteTypeFor(commonMasks.syncStarIterableType);
+ }
+
+ TypeInformation asyncStarStreamTypeCache;
+ TypeInformation get asyncStarStreamType {
+ if (asyncStarStreamTypeCache != null) return asyncStarStreamTypeCache;
+ return asyncStarStreamTypeCache =
+ getConcreteTypeFor(commonMasks.asyncStarStreamType);
+ }
+
+ TypeInformation nonNullEmptyType;
+
+ TypeInformation stringLiteralType(DartString value) {
+ return new StringLiteralTypeInformation(value, commonMasks.stringType);
+ }
+
+ TypeInformation boolLiteralType(ast.LiteralBool value) {
+ return new BoolLiteralTypeInformation(value, commonMasks.boolType);
+ }
+
+ /**
+ * Returns the least upper bound between [firstType] and
+ * [secondType].
+ */
+ TypeInformation computeLUB(
+ TypeInformation firstType, TypeInformation secondType) {
+ if (firstType == null) return secondType;
+ if (firstType == secondType) return firstType;
+ if (firstType == nonNullEmptyType) return secondType;
+ if (secondType == nonNullEmptyType) return firstType;
+ if (firstType == dynamicType || secondType == dynamicType) {
+ return dynamicType;
+ }
+ return getConcreteTypeFor(
+ firstType.type.union(secondType.type, closedWorld));
+ }
+
+ /**
+ * Returns `true` if `selector` should be updated to reflect the new
+ * `receiverType`.
+ */
+ bool selectorNeedsUpdate(TypeInformation info, TypeMask mask) {
+ return info.type != mask;
+ }
+
+ /**
+ * Returns a new receiver type for this [selector] applied to
+ * [receiverType].
+ *
+ * The option [isConditional] is true when [selector] was seen in a
+ * conditional send (e.g. `a?.selector`), in which case the returned type may
+ * be null.
+ */
+ TypeInformation refineReceiver(Selector selector, TypeMask mask,
+ TypeInformation receiver, bool isConditional) {
+ if (receiver.type.isExact) return receiver;
+ TypeMask otherType = closedWorld.allFunctions.receiverType(selector, mask);
+ // Conditional sends (a?.b) can still narrow the possible types of `a`,
+ // however, we still need to consider that `a` may be null.
+ if (isConditional) {
+ // Note: we don't check that receiver.type.isNullable here because this is
+ // called during the graph construction.
+ otherType = otherType.nullable();
+ }
+ // If this is refining to nullable subtype of `Object` just return
+ // the receiver. We know the narrowing is useless.
+ if (otherType.isNullable && otherType.containsAll(closedWorld)) {
+ return receiver;
+ }
+ assert(TypeMask.assertIsNormalized(otherType, closedWorld));
+ TypeInformation newType = new NarrowTypeInformation(receiver, otherType);
+ allocatedTypes.add(newType);
+ return newType;
+ }
+
+ /**
+ * Returns the intersection between [type] and [annotation].
+ * [isNullable] indicates whether the annotation implies a null
+ * type.
+ */
+ TypeInformation narrowType(
+ TypeInformation type, ResolutionDartType annotation,
+ {bool isNullable: true}) {
+ if (annotation.treatAsDynamic) return type;
+ if (annotation.isVoid) return nullType;
+ if (annotation.element == closedWorld.commonElements.objectClass &&
+ isNullable) {
+ return type;
+ }
+ TypeMask otherType;
+ if (annotation.isTypedef || annotation.isFunctionType) {
+ otherType = functionType.type;
+ } else if (annotation.isTypeVariable) {
+ // TODO(ngeoffray): Narrow to bound.
+ return type;
+ } else {
+ assert(annotation.isInterfaceType);
+ otherType = annotation.element == closedWorld.commonElements.objectClass
+ ? dynamicType.type.nonNullable()
+ : new TypeMask.nonNullSubtype(annotation.element, closedWorld);
+ }
+ if (isNullable) otherType = otherType.nullable();
+ if (type.type.isExact) {
+ return type;
+ } else {
+ assert(TypeMask.assertIsNormalized(otherType, closedWorld));
+ TypeInformation newType = new NarrowTypeInformation(type, otherType);
+ allocatedTypes.add(newType);
+ return newType;
+ }
+ }
+
+ /**
+ * Returns the non-nullable type of [type].
+ */
+ TypeInformation narrowNotNull(TypeInformation type) {
+ if (type.type.isExact && !type.type.isNullable) {
+ return type;
+ }
+ TypeInformation newType =
+ new NarrowTypeInformation(type, dynamicType.type.nonNullable());
+ allocatedTypes.add(newType);
+ return newType;
+ }
+
+ ElementTypeInformation getInferredTypeOf(Element element) {
+ element = element.implementation;
+ return typeInformations.putIfAbsent(element, () {
+ return new ElementTypeInformation(element, this);
+ });
+ }
+
+ /**
+ * Returns the internal inferrer representation for [mask].
+ */
+ ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) {
+ assert(mask != null);
+ return concreteTypes.putIfAbsent(mask, () {
+ return new ConcreteTypeInformation(mask);
+ });
+ }
+
+ String getInferredSignatureOf(FunctionElement function) {
+ ElementTypeInformation info = getInferredTypeOf(function);
+ FunctionElement impl = function.implementation;
+ FunctionSignature signature = impl.functionSignature;
+ var res = "";
+ signature.forEachParameter((Element parameter) {
+ TypeInformation type = getInferredTypeOf(parameter);
+ res += "${res.isEmpty ? '(' : ', '}${type.type} ${parameter.name}";
+ });
+ res += ") -> ${info.type}";
+ return res;
+ }
+
+ TypeInformation nonNullSubtype(ClassElement type) {
+ return getConcreteTypeFor(
+ new TypeMask.nonNullSubtype(type.declaration, closedWorld));
+ }
+
+ TypeInformation nonNullSubclass(ClassElement type) {
+ return getConcreteTypeFor(
+ new TypeMask.nonNullSubclass(type.declaration, closedWorld));
+ }
+
+ TypeInformation nonNullExact(ClassElement type) {
+ return getConcreteTypeFor(
+ new TypeMask.nonNullExact(type.declaration, closedWorld));
+ }
+
+ TypeInformation nonNullEmpty() {
+ return nonNullEmptyType;
+ }
+
+ bool isNull(TypeInformation type) {
+ return type == nullType;
+ }
+
+ TypeInformation allocateList(
+ TypeInformation type, ast.Node node, Element enclosing,
+ [TypeInformation elementType, int length]) {
+ ClassElement typedDataClass = closedWorld.commonElements.typedDataClass;
+ bool isTypedArray = typedDataClass != null &&
+ closedWorld.isInstantiated(typedDataClass) &&
+ type.type.satisfies(typedDataClass, closedWorld);
+ bool isConst = (type.type == commonMasks.constListType);
+ bool isFixed =
+ (type.type == commonMasks.fixedListType) || isConst || isTypedArray;
+ bool isElementInferred = isConst || isTypedArray;
+
+ int inferredLength = isFixed ? length : null;
+ TypeMask elementTypeMask =
+ isElementInferred ? elementType.type : dynamicType.type;
+ ContainerTypeMask mask = new ContainerTypeMask(
+ type.type, node, enclosing, elementTypeMask, inferredLength);
+ ElementInContainerTypeInformation element =
+ new ElementInContainerTypeInformation(currentMember, elementType);
+ element.inferred = isElementInferred;
+
+ allocatedTypes.add(element);
+ return allocatedLists[node] =
+ new ListTypeInformation(currentMember, mask, element, length);
+ }
+
+ TypeInformation allocateClosure(ast.Node node, Element element) {
+ TypeInformation result =
+ new ClosureTypeInformation(currentMember, node, element);
+ allocatedClosures.add(result);
+ return result;
+ }
+
+ TypeInformation allocateMap(
+ ConcreteTypeInformation type, ast.Node node, Element element,
+ [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) {
+ assert(keyTypes.length == valueTypes.length);
+ bool isFixed = (type.type == commonMasks.constMapType);
+
+ TypeMask keyType, valueType;
+ if (isFixed) {
+ keyType = keyTypes.fold(nonNullEmptyType.type,
+ (type, info) => type.union(info.type, closedWorld));
+ valueType = valueTypes.fold(nonNullEmptyType.type,
+ (type, info) => type.union(info.type, closedWorld));
+ } else {
+ keyType = valueType = dynamicType.type;
+ }
+ MapTypeMask mask =
+ new MapTypeMask(type.type, node, element, keyType, valueType);
+
+ TypeInformation keyTypeInfo =
+ new KeyInMapTypeInformation(currentMember, null);
+ TypeInformation valueTypeInfo =
+ new ValueInMapTypeInformation(currentMember, null);
+ allocatedTypes.add(keyTypeInfo);
+ allocatedTypes.add(valueTypeInfo);
+
+ MapTypeInformation map =
+ new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
+
+ for (int i = 0; i < keyTypes.length; ++i) {
+ TypeInformation newType =
+ map.addEntryAssignment(keyTypes[i], valueTypes[i], true);
+ if (newType != null) allocatedTypes.add(newType);
+ }
+
+ // Shortcut: If we already have a first approximation of the key/value type,
+ // start propagating it early.
+ if (isFixed) map.markAsInferred();
+
+ allocatedMaps[node] = map;
+ return map;
+ }
+
+ TypeMask newTypedSelector(TypeInformation info, TypeMask mask) {
+ // Only type the selector if [info] is concrete, because the other
+ // kinds of [TypeInformation] have the empty type at this point of
+ // analysis.
+ return info.isConcrete ? info.type : mask;
+ }
+
+ /**
+ * Returns a new type that unions [firstInput] and [secondInput].
+ */
+ TypeInformation allocateDiamondPhi(
+ TypeInformation firstInput, TypeInformation secondInput) {
+ PhiElementTypeInformation result =
+ new PhiElementTypeInformation(currentMember, null, false, null);
+ result.addAssignment(firstInput);
+ result.addAssignment(secondInput);
+ allocatedTypes.add(result);
+ return result;
+ }
+
+ PhiElementTypeInformation _addPhi(
+ ast.Node node, Local variable, inputType, bool isLoop) {
+ PhiElementTypeInformation result =
+ new PhiElementTypeInformation(currentMember, node, isLoop, variable);
+ allocatedTypes.add(result);
+ result.addAssignment(inputType);
+ return result;
+ }
+
+ /**
+ * Returns a new type for holding the potential types of [element].
+ * [inputType] is the first incoming type of the phi.
+ */
+ PhiElementTypeInformation allocatePhi(
+ ast.Node node, Local variable, inputType) {
+ // Check if [inputType] is a phi for a local updated in
+ // the try/catch block [node]. If it is, no need to allocate a new
+ // phi.
+ if (inputType is PhiElementTypeInformation &&
+ inputType.branchNode == node &&
+ inputType.branchNode is ast.TryStatement) {
+ return inputType;
+ }
+ return _addPhi(node, variable, inputType, false);
+ }
+
+ /**
+ * Returns a new type for holding the potential types of [element].
+ * [inputType] is the first incoming type of the phi. [allocateLoopPhi]
+ * only differs from [allocatePhi] in that it allows the underlying
+ * implementation of [TypeSystem] to differentiate Phi nodes due to loops
+ * from other merging uses.
+ */
+ PhiElementTypeInformation allocateLoopPhi(
+ ast.Node node, Local variable, inputType) {
+ return _addPhi(node, variable, inputType, true);
+ }
+
+ /**
+ * Simplies the phi representing [element] and of the type
+ * [phiType]. For example, if this phi has one incoming input, an
+ * implementation of this method could just return that incoming
+ * input type.
+ */
+ TypeInformation simplifyPhi(
+ ast.Node node, Local variable, PhiElementTypeInformation phiType) {
+ assert(phiType.branchNode == node);
+ if (phiType.assignments.length == 1) return phiType.assignments.first;
+ return phiType;
+ }
+
+ /**
+ * Adds [newType] as an input of [phiType].
+ */
+ PhiElementTypeInformation addPhiInput(Local variable,
+ PhiElementTypeInformation phiType, TypeInformation newType) {
+ phiType.addAssignment(newType);
+ return phiType;
+ }
+
+ TypeMask computeTypeMask(Iterable<TypeInformation> assignments) {
+ return joinTypeMasks(assignments.map((e) => e.type));
+ }
+
+ TypeMask joinTypeMasks(Iterable<TypeMask> masks) {
+ var dynamicType = commonMasks.dynamicType;
+ // Optimization: we are iterating over masks twice, but because `masks` is a
+ // mapped iterable, we save the intermediate results to avoid computing them
+ // again.
+ var list = [];
+ for (TypeMask mask in masks) {
+ // Don't do any work on computing unions if we know that after all that
+ // work the result will be `dynamic`.
+ // TODO(sigmund): change to `mask == dynamicType` so we can continue to
+ // track the non-nullable bit.
+ if (mask.containsAll(closedWorld)) return dynamicType;
+ list.add(mask);
+ }
+
+ TypeMask newType = null;
+ for (TypeMask mask in list) {
+ newType = newType == null ? mask : newType.union(mask, closedWorld);
+ // Likewise - stop early if we already reach dynamic.
+ if (newType.containsAll(closedWorld)) return dynamicType;
+ }
+
+ return newType ?? const TypeMask.nonNullEmpty();
+ }
+}
« no previous file with comments | « pkg/compiler/lib/src/inferrer/type_graph_nodes.dart ('k') | pkg/compiler/lib/src/types/types.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698