Use AbstractValue in the rest of type inference

Change-Id: Ic782af55e7b9ab9942eab9809f180e3979d2b326
Reviewed-on: https://dart-review.googlesource.com/56920
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index c43f13f9..e0b8077 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -20,7 +20,7 @@
 import 'elements/entities.dart';
 import 'js/js.dart' as jsAst;
 import 'js_backend/js_backend.dart' show JavaScriptBackend;
-import 'types/masks.dart';
+import 'types/abstract_value_domain.dart';
 import 'types/types.dart'
     show GlobalTypeInferenceElementResult, GlobalTypeInferenceMemberResult;
 import 'universe/world_builder.dart' show CodegenWorldBuilder;
@@ -118,9 +118,12 @@
     if (!isInInstantiatedClass && !_hasBeenResolved(field)) {
       return null;
     }
-    TypeMask inferredType = _resultOfMember(field).type;
+    AbstractValue inferredType = _resultOfMember(field).type;
     // If a field has an empty inferred type it is never used.
-    if (inferredType == null || inferredType.isEmpty) return null;
+    if (inferredType == null ||
+        closedWorld.abstractValueDomain.isEmpty(inferredType)) {
+      return null;
+    }
 
     int size = compiler.dumpInfoTask.sizeOf(field);
     String code = compiler.dumpInfoTask.codeOf(field);
@@ -461,13 +464,12 @@
         entity,
         impact,
         new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) {
-          TypeMask mask = dynamicUse.receiverConstraint;
+          AbstractValue mask = dynamicUse.receiverConstraint;
           selections.addAll(closedWorld
               // TODO(het): Handle `call` on `Closure` through
               // `world.includesClosureCall`.
               .locateMembers(dynamicUse.selector, mask)
-              .map((MemberEntity e) =>
-                  new Selection(e, dynamicUse.receiverConstraint)));
+              .map((MemberEntity e) => new Selection(e, mask)));
         }, visitStaticUse: (staticUse) {
           selections.add(new Selection(staticUse.element, null));
         }),
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 0764bff..5abfae9 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -440,8 +440,8 @@
 
     info.bailedOut = false;
     info.elementType.inferred = true;
-    AbstractValue fixedListType = abstractValueDomain.fixedListType;
-    if (info.originalType.forwardTo == fixedListType) {
+    if (abstractValueDomain.isSpecializationOf(
+        info.originalType, abstractValueDomain.fixedListType)) {
       info.checksGrowable = tracer.callsGrowableMethod;
     }
     tracer.assignments.forEach(info.elementType.addAssignment);
@@ -460,12 +460,12 @@
 
     info.bailedOut = false;
     for (int i = 0; i < tracer.keyAssignments.length; ++i) {
-      TypeInformation newType = info.addEntryAssignment(
+      TypeInformation newType = info.addEntryAssignment(abstractValueDomain,
           tracer.keyAssignments[i], tracer.valueAssignments[i]);
       if (newType != null) workQueue.add(newType);
     }
     for (TypeInformation map in tracer.mapAssignments) {
-      workQueue.addAll(info.addMapAssignment(map));
+      workQueue.addAll(info.addMapAssignment(abstractValueDomain, map));
     }
 
     info.markAsInferred();
@@ -596,15 +596,15 @@
       types.allocatedLists.values.forEach((_info) {
         ListTypeInformation info = _info;
         print('${info.type} '
-            'for ${info.originalType.allocationNode} '
-            'at ${info.originalType.allocationElement} '
+            'for ${abstractValueDomain.getAllocationNode(info.originalType)} '
+            'at ${abstractValueDomain.getAllocationElement(info.originalType)}'
             'after ${info.refineCount}');
       });
       types.allocatedMaps.values.forEach((_info) {
         MapTypeInformation info = _info;
         print('${info.type} '
-            'for ${info.originalType.allocationNode} '
-            'at ${info.originalType.allocationElement} '
+            'for ${abstractValueDomain.getAllocationNode(info.originalType)} '
+            'at ${abstractValueDomain.getAllocationElement(info.originalType)}'
             'after ${info.refineCount}');
       });
       types.allocatedClosures.forEach((TypeInformation info) {
@@ -711,7 +711,8 @@
                 // the old type around to ensure that we get a complete view
                 // of the type graph and do not drop any flow edges.
                 AbstractValue refinedType = computeTypeMask(closedWorld, value);
-                type = new NarrowTypeInformation(type, refinedType);
+                type = new NarrowTypeInformation(
+                    abstractValueDomain, type, refinedType);
                 types.allocatedTypes.add(type);
               }
             }
@@ -766,7 +767,8 @@
       if (info is StaticCallSiteTypeInformation) {
         MemberEntity member = info.calledElement;
         closedWorldRefiner.addFunctionCalledInLoop(member);
-      } else if (info.mask != null && !info.mask.containsAll(closedWorld)) {
+      } else if (info.mask != null &&
+          !abstractValueDomain.containsAll(info.mask)) {
         // For instance methods, we only register a selector called in a
         // loop if it is a typed selector, to avoid marking too many
         // methods as being called from within a loop. This cuts down
@@ -906,7 +908,8 @@
 
   TypeInformation getDefaultTypeOfParameter(Local parameter) {
     return defaultTypeOfParameter.putIfAbsent(parameter, () {
-      return new PlaceholderTypeInformation(types.currentMember);
+      return new PlaceholderTypeInformation(
+          abstractValueDomain, types.currentMember);
     });
   }
 
@@ -967,6 +970,7 @@
       SideEffectsBuilder sideEffectsBuilder,
       bool inLoop) {
     CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
+        abstractValueDomain,
         types.currentMember,
         node,
         caller,
@@ -1020,6 +1024,7 @@
     });
 
     CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+        abstractValueDomain,
         types.currentMember,
         callType,
         node,
@@ -1037,16 +1042,16 @@
   }
 
   TypeInformation registerAwait(T node, TypeInformation argument) {
-    AwaitTypeInformation info =
-        new AwaitTypeInformation<T>(types.currentMember, node);
+    AwaitTypeInformation info = new AwaitTypeInformation<T>(
+        abstractValueDomain, types.currentMember, node);
     info.addAssignment(argument);
     types.allocatedTypes.add(info);
     return info;
   }
 
   TypeInformation registerYield(T node, TypeInformation argument) {
-    YieldTypeInformation info =
-        new YieldTypeInformation<T>(types.currentMember, node);
+    YieldTypeInformation info = new YieldTypeInformation<T>(
+        abstractValueDomain, types.currentMember, node);
     info.addAssignment(argument);
     types.allocatedTypes.add(info);
     return info;
@@ -1063,6 +1068,7 @@
       {bool inLoop}) {
     sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
     CallSiteTypeInformation info = new ClosureCallSiteTypeInformation(
+        abstractValueDomain,
         types.currentMember,
         node,
         caller,
diff --git a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart
index 7b49435..a5f708c 100644
--- a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart
@@ -268,7 +268,9 @@
 
   @override
   ParameterTypeInformation createParameterTypeInformation(
-      covariant JLocal parameter, TypeSystem<ir.Node> types) {
+      AbstractValueDomain abstractValueDomain,
+      covariant JLocal parameter,
+      TypeSystem<ir.Node> types) {
     MemberEntity context = parameter.memberContext;
     KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(context);
     ir.FunctionNode functionNode =
@@ -289,40 +291,48 @@
         types.getInferredTypeOfMember(member);
     if (isClosure) {
       return new ParameterTypeInformation.localFunction(
-          memberTypeInformation, parameter, type, member);
+          abstractValueDomain, memberTypeInformation, parameter, type, member);
     } else if (member.isInstanceMember) {
-      return new ParameterTypeInformation.instanceMember(memberTypeInformation,
-          parameter, type, member, new ParameterAssignments());
+      return new ParameterTypeInformation.instanceMember(
+          abstractValueDomain,
+          memberTypeInformation,
+          parameter,
+          type,
+          member,
+          new ParameterAssignments());
     } else {
       return new ParameterTypeInformation.static(
-          memberTypeInformation, parameter, type, member);
+          abstractValueDomain, memberTypeInformation, parameter, type, member);
     }
   }
 
   @override
-  MemberTypeInformation createMemberTypeInformation(MemberEntity member) {
+  MemberTypeInformation createMemberTypeInformation(
+      AbstractValueDomain abstractValueDomain, MemberEntity member) {
     if (member.isField) {
       FieldEntity field = member;
       DartType type = _elementEnvironment.getFieldType(field);
-      return new FieldTypeInformation(field, type);
+      return new FieldTypeInformation(abstractValueDomain, field, type);
     } else if (member.isGetter) {
       FunctionEntity getter = member;
       DartType type = _elementEnvironment.getFunctionType(getter);
-      return new GetterTypeInformation(getter, type);
+      return new GetterTypeInformation(abstractValueDomain, getter, type);
     } else if (member.isSetter) {
       FunctionEntity setter = member;
-      return new SetterTypeInformation(setter);
+      return new SetterTypeInformation(abstractValueDomain, setter);
     } else if (member.isFunction) {
       FunctionEntity method = member;
       DartType type = _elementEnvironment.getFunctionType(method);
-      return new MethodTypeInformation(method, type);
+      return new MethodTypeInformation(abstractValueDomain, method, type);
     } else {
       ConstructorEntity constructor = member;
       if (constructor.isFactoryConstructor) {
         DartType type = _elementEnvironment.getFunctionType(constructor);
-        return new FactoryConstructorTypeInformation(constructor, type);
+        return new FactoryConstructorTypeInformation(
+            abstractValueDomain, constructor, type);
       } else {
-        return new GenerativeConstructorTypeInformation(constructor);
+        return new GenerativeConstructorTypeInformation(
+            abstractValueDomain, constructor);
       }
     }
   }
diff --git a/pkg/compiler/lib/src/inferrer/node_tracer.dart b/pkg/compiler/lib/src/inferrer/node_tracer.dart
index 032b254..b09dfcc 100644
--- a/pkg/compiler/lib/src/inferrer/node_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/node_tracer.dart
@@ -6,7 +6,7 @@
 
 import '../common/names.dart' show Identifiers;
 import '../elements/entities.dart';
-import '../types/masks.dart' show ContainerTypeMask, MapTypeMask;
+import '../types/abstract_value_domain.dart';
 import '../util/util.dart' show Setlet;
 import 'debug.dart' as debug;
 import 'inferrer_engine.dart';
@@ -306,31 +306,33 @@
 
   void visitDynamicCallSiteTypeInformation(
       DynamicCallSiteTypeInformation info) {
-    void addsToContainer(ContainerTypeMask mask) {
-      if (mask.allocationNode != null) {
+    void addsToContainer(AbstractValue mask) {
+      Object allocationNode =
+          inferrer.abstractValueDomain.getAllocationNode(mask);
+      if (allocationNode != null) {
         ListTypeInformation list =
-            inferrer.types.allocatedLists[mask.allocationNode];
+            inferrer.types.allocatedLists[allocationNode];
         listsToAnalyze.add(list);
       } else {
-        // The [ContainerTypeMask] is a union of two containers, and we lose
-        // track of where these containers have been allocated at this point.
+        // The [mask] is a union of two containers, and we lose track of where
+        // these containers have been allocated at this point.
         bailout('Stored in too many containers');
       }
     }
 
-    void addsToMapValue(MapTypeMask mask) {
-      if (mask.allocationNode != null) {
-        MapTypeInformation map =
-            inferrer.types.allocatedMaps[mask.allocationNode];
+    void addsToMapValue(AbstractValue mask) {
+      Object allocationNode =
+          inferrer.abstractValueDomain.getAllocationNode(mask);
+      if (allocationNode != null) {
+        MapTypeInformation map = inferrer.types.allocatedMaps[allocationNode];
         mapsToAnalyze.add(map);
       } else {
-        // The [MapTypeMask] is a union. See comment for [ContainerTypeMask]
-        // above.
+        // The [mask] is a union. See comment for [mask] above.
         bailout('Stored in too many maps');
       }
     }
 
-    void addsToMapKey(MapTypeMask mask) {
+    void addsToMapKey(AbstractValue mask) {
       // We do not track the use of keys from a map, so we have to bail.
       bailout('Used as key in Map');
     }
@@ -338,9 +340,9 @@
     // "a[...] = x" could be a list (container) or map assignemnt.
     if (isIndexSetValue(info)) {
       var receiverType = info.receiver.type;
-      if (receiverType is ContainerTypeMask) {
+      if (inferrer.abstractValueDomain.isContainer(receiverType)) {
         addsToContainer(receiverType);
-      } else if (receiverType is MapTypeMask) {
+      } else if (inferrer.abstractValueDomain.isMap(receiverType)) {
         addsToMapValue(receiverType);
       } else {
         // Not a container or map, so the targets could be any methods. There
@@ -363,7 +365,7 @@
     // Could be:  m[x] = ...;
     if (isIndexSetKey(info)) {
       var receiverType = info.receiver.type;
-      if (receiverType is MapTypeMask) {
+      if (inferrer.abstractValueDomain.isMap(receiverType)) {
         addsToMapKey(receiverType);
       } else {
         bailoutIfReaches(isParameterOfListAddingMethod);
@@ -373,7 +375,7 @@
 
     if (mightAddToContainer(info)) {
       var receiverType = info.receiver.type;
-      if (receiverType is ContainerTypeMask) {
+      if (inferrer.abstractValueDomain.isContainer(receiverType)) {
         addsToContainer(receiverType);
       } else {
         // Not a container, see note above.
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
index d8242e9..52ff120 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
@@ -6,7 +6,7 @@
 import '../../compiler_new.dart';
 import '../elements/entities.dart';
 import '../elements/entity_utils.dart' as utils;
-import '../types/masks.dart';
+import '../types/abstract_value_domain.dart';
 import 'inferrer_engine.dart';
 import 'type_graph_nodes.dart';
 import 'debug.dart';
@@ -80,7 +80,8 @@
         String name = filenameFromElement(element);
         output = compilerOutput.createOutputSink(
             '$outputDir/$name', 'dot', OutputType.debug);
-        _GraphGenerator visitor = new _GraphGenerator(this, element, output);
+        _GraphGenerator visitor = new _GraphGenerator(
+            this, element, output, inferrer.abstractValueDomain.getCompactText);
         for (TypeInformation node in nodes[element]) {
           visitor.visit(node);
         }
@@ -137,12 +138,13 @@
   final Set<TypeInformation> seen = new Set<TypeInformation>();
   final List<TypeInformation> worklist = new List<TypeInformation>();
   final Map<TypeInformation, int> nodeId = <TypeInformation, int>{};
+  final String Function(AbstractValue) formatType;
   int usedIds = 0;
   final OutputSink output;
   final MemberEntity element;
   TypeInformation returnValue;
 
-  _GraphGenerator(this.global, this.element, this.output) {
+  _GraphGenerator(this.global, this.element, this.output, this.formatType) {
     returnValue = global.inferrer.types.getInferredTypeOfMember(element);
     getNode(returnValue); // Ensure return value is part of graph.
     append('digraph {');
@@ -413,41 +415,3 @@
     addNode(info, 'Yield\n$text');
   }
 }
-
-/// Convert the given TypeMask to a compact string format.
-///
-/// The default format is too verbose for the graph format since long strings
-/// create oblong nodes that obstruct the graph layout.
-String formatType(TypeMask type) {
-  if (type is FlatTypeMask) {
-    // TODO(asgerf): Disambiguate classes whose name is not unique. Using the
-    //     library name for all classes is not a good idea, since library names
-    //     can be really long and mess up the layout.
-    // Capitalize Null to emphasize that it's the null type mask and not
-    // a null value we accidentally printed out.
-    if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty';
-    String nullFlag = type.isNullable ? '?' : '';
-    String subFlag = type.isExact ? '' : type.isSubclass ? '+' : '*';
-    return '${type.base.name}$nullFlag$subFlag';
-  }
-  if (type is UnionTypeMask) {
-    return type.disjointMasks.map(formatType).join(' | ');
-  }
-  if (type is ContainerTypeMask) {
-    String container = formatType(type.forwardTo);
-    String member = formatType(type.elementType);
-    return '$container<$member>';
-  }
-  if (type is MapTypeMask) {
-    String container = formatType(type.forwardTo);
-    String key = formatType(type.keyType);
-    String value = formatType(type.valueType);
-    return '$container<$key,$value>';
-  }
-  if (type is ValueTypeMask) {
-    String baseType = formatType(type.forwardTo);
-    String value = type.value.toStructuredText();
-    return '$baseType=$value';
-  }
-  return '$type'; // Fall back on toString if not supported here.
-}
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index cb9971a..2353af8 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -12,14 +12,7 @@
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart';
-import '../types/masks.dart'
-    show
-        CommonMasks,
-        ContainerTypeMask,
-        DictionaryTypeMask,
-        MapTypeMask,
-        TypeMask,
-        ValueTypeMask;
+import '../types/abstract_value_domain.dart';
 import '../universe/selector.dart' show Selector;
 import '../util/util.dart' show ImmutableEmptySet, Setlet;
 import '../world.dart' show ClosedWorld;
@@ -50,7 +43,7 @@
 
   /// The type the inferrer has found for this [TypeInformation].
   /// Initially empty.
-  TypeMask type = const TypeMask.nonNullEmpty();
+  AbstractValue type;
 
   /// The graph node of the member this [TypeInformation] node belongs to.
   final MemberTypeInformation context;
@@ -92,20 +85,20 @@
 
   bool get isConcrete => false;
 
-  TypeInformation(this.context)
+  TypeInformation(this.type, this.context)
       : _assignments = <TypeInformation>[],
         users = new Setlet<TypeInformation>();
 
-  TypeInformation.noAssignments(this.context)
+  TypeInformation.noAssignments(this.type, this.context)
       : _assignments = const <TypeInformation>[],
         users = new Setlet<TypeInformation>();
 
-  TypeInformation.untracked()
+  TypeInformation.untracked(this.type)
       : _assignments = const <TypeInformation>[],
         users = const ImmutableEmptySet(),
         context = null;
 
-  TypeInformation.withAssignments(this.context, this._assignments)
+  TypeInformation.withAssignments(this.type, this.context, this._assignments)
       : users = new Setlet<TypeInformation>();
 
   void addUser(TypeInformation user) {
@@ -152,7 +145,7 @@
     }
   }
 
-  TypeMask refine(InferrerEngine inferrer) {
+  AbstractValue refine(InferrerEngine inferrer) {
     return abandonInferencing ? safeType(inferrer) : computeType(inferrer);
   }
 
@@ -160,13 +153,13 @@
    * Computes a new type for this [TypeInformation] node depending on its
    * potentially updated inputs.
    */
-  TypeMask computeType(InferrerEngine inferrer);
+  AbstractValue computeType(InferrerEngine inferrer);
 
   /**
    * Returns an approximation for this [TypeInformation] node that is always
    * safe to use. Used when abandoning inference on a node.
    */
-  TypeMask safeType(InferrerEngine inferrer) {
+  AbstractValue safeType(InferrerEngine inferrer) {
     return inferrer.types.dynamicType.type;
   }
 
@@ -189,7 +182,7 @@
 
   bool reset(InferrerEngine inferrer) {
     if (abandonInferencing) return false;
-    type = const TypeMask.nonNullEmpty();
+    type = inferrer.abstractValueDomain.emptyType;
     refineCount = 0;
     return true;
   }
@@ -259,13 +252,15 @@
  * [getDefaultTypeOfParameter] and [setDefaultTypeOfParameter] for details.
  */
 class PlaceholderTypeInformation extends TypeInformation {
-  PlaceholderTypeInformation(MemberTypeInformation context) : super(context);
+  PlaceholderTypeInformation(
+      AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
+      : super(abstractValueDomain.emptyType, context);
 
   void accept(TypeInformationVisitor visitor) {
     throw new UnsupportedError("Cannot visit placeholder");
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
     throw new UnsupportedError("Cannot refine placeholder");
   }
 
@@ -350,11 +345,15 @@
   /// Marker to disable inference for closures in [handleSpecialCases].
   bool disableInferenceForClosures = true;
 
-  ElementTypeInformation._internal(MemberTypeInformation context)
-      : super(context);
+  ElementTypeInformation._internal(
+      AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
+      : super(abstractValueDomain.emptyType, context);
   ElementTypeInformation._withAssignments(
-      MemberTypeInformation context, ParameterAssignments assignments)
-      : super.withAssignments(context, assignments);
+      AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context,
+      ParameterAssignments assignments)
+      : super.withAssignments(
+            abstractValueDomain.emptyType, context, assignments);
 
   String getInferredSignature(TypeSystem types);
 
@@ -399,7 +398,9 @@
    */
   Map<MemberEntity, Setlet<Object>> _callers;
 
-  MemberTypeInformation._internal(this._member) : super._internal(null);
+  MemberTypeInformation._internal(
+      AbstractValueDomain abstractValueDomain, this._member)
+      : super._internal(abstractValueDomain, null);
 
   MemberEntity get member => _member;
 
@@ -452,9 +453,9 @@
   // state of the [isStable] field inherited from [TypeInformation].
   bool get isStable => super.isStable && !isClosurized;
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer);
+  AbstractValue handleSpecialCases(InferrerEngine inferrer);
 
-  TypeMask _handleFunctionCase(
+  AbstractValue _handleFunctionCase(
       FunctionEntity function, InferrerEngine inferrer) {
     if (inferrer.closedWorld.nativeData.isNativeMember(function)) {
       // Use the type annotation as the type for native elements. We
@@ -469,7 +470,8 @@
     return null;
   }
 
-  TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     if (inferrer.options.assignmentCheckPolicy.isTrusted ||
         inferrer.options.assignmentCheckPolicy.isEmitted ||
         inferrer.trustTypeAnnotations(_member)) {
@@ -478,16 +480,17 @@
     return mask;
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer);
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer);
 
-  TypeMask computeType(InferrerEngine inferrer) {
-    TypeMask special = handleSpecialCases(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) {
+    AbstractValue special = handleSpecialCases(inferrer);
     if (special != null) return potentiallyNarrowType(special, inferrer);
     return potentiallyNarrowType(
         inferrer.types.computeTypeMask(assignments), inferrer);
   }
 
-  TypeMask safeType(InferrerEngine inferrer) {
+  AbstractValue safeType(InferrerEngine inferrer) {
     return potentiallyNarrowType(super.safeType(inferrer), inferrer);
   }
 
@@ -515,10 +518,11 @@
   FieldEntity get _field => _member;
   final DartType _type;
 
-  FieldTypeInformation(FieldEntity element, this._type)
-      : super._internal(element);
+  FieldTypeInformation(
+      AbstractValueDomain abstractValueDomain, FieldEntity element, this._type)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     if (!inferrer.canFieldBeUsedForGlobalOptimizations(_field) ||
         inferrer.assumeDynamic(_field)) {
       // Do not infer types for fields that have a corresponding annotation or
@@ -540,7 +544,8 @@
     return null;
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return _narrowType(inferrer.closedWorld, mask, _type);
   }
 
@@ -558,14 +563,16 @@
   FunctionEntity get _getter => _member;
   final FunctionType _type;
 
-  GetterTypeInformation(FunctionEntity element, this._type)
-      : super._internal(element);
+  GetterTypeInformation(AbstractValueDomain abstractValueDomain,
+      FunctionEntity element, this._type)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     return _handleFunctionCase(_getter, inferrer);
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return _narrowType(inferrer.closedWorld, mask, _type.returnType);
   }
 }
@@ -573,13 +580,16 @@
 class SetterTypeInformation extends MemberTypeInformation {
   FunctionEntity get _setter => _member;
 
-  SetterTypeInformation(FunctionEntity element) : super._internal(element);
+  SetterTypeInformation(
+      AbstractValueDomain abstractValueDomain, FunctionEntity element)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     return _handleFunctionCase(_setter, inferrer);
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return mask;
   }
 }
@@ -588,14 +598,16 @@
   FunctionEntity get _method => _member;
   final FunctionType _type;
 
-  MethodTypeInformation(FunctionEntity element, this._type)
-      : super._internal(element);
+  MethodTypeInformation(AbstractValueDomain abstractValueDomain,
+      FunctionEntity element, this._type)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     return _handleFunctionCase(_method, inferrer);
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return _narrowType(inferrer.closedWorld, mask, _type.returnType);
   }
 
@@ -606,29 +618,31 @@
   ConstructorEntity get _constructor => _member;
   final FunctionType _type;
 
-  FactoryConstructorTypeInformation(ConstructorEntity element, this._type)
-      : super._internal(element);
+  FactoryConstructorTypeInformation(AbstractValueDomain abstractValueDomain,
+      ConstructorEntity element, this._type)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
-    CommonMasks abstractValueDomain = inferrer.abstractValueDomain;
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
+    AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
     if (_constructor.isFromEnvironmentConstructor) {
       if (_constructor.enclosingClass == inferrer.commonElements.intClass) {
         giveUp(inferrer);
-        return abstractValueDomain.intType.nullable();
+        return abstractValueDomain.includeNull(abstractValueDomain.intType);
       } else if (_constructor.enclosingClass ==
           inferrer.commonElements.boolClass) {
         giveUp(inferrer);
-        return abstractValueDomain.boolType.nullable();
+        return abstractValueDomain.includeNull(abstractValueDomain.boolType);
       } else if (_constructor.enclosingClass ==
           inferrer.commonElements.stringClass) {
         giveUp(inferrer);
-        return abstractValueDomain.stringType.nullable();
+        return abstractValueDomain.includeNull(abstractValueDomain.stringType);
       }
     }
     return _handleFunctionCase(_constructor, inferrer);
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return _narrowType(inferrer.closedWorld, mask, _type.returnType);
   }
 
@@ -640,14 +654,16 @@
 class GenerativeConstructorTypeInformation extends MemberTypeInformation {
   ConstructorEntity get _constructor => _member;
 
-  GenerativeConstructorTypeInformation(ConstructorEntity element)
-      : super._internal(element);
+  GenerativeConstructorTypeInformation(
+      AbstractValueDomain abstractValueDomain, ConstructorEntity element)
+      : super._internal(abstractValueDomain, element);
 
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     return _handleFunctionCase(_constructor, inferrer);
   }
 
-  TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue _potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     return mask;
   }
 
@@ -675,21 +691,26 @@
   bool _isTearOffClosureParameter = false;
 
   ParameterTypeInformation.localFunction(
-      MemberTypeInformation context, this._parameter, this._type, this._method)
+      AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context,
+      this._parameter,
+      this._type,
+      this._method)
       : _isInstanceMemberParameter = false,
         _isClosureParameter = true,
         _isInitializingFormal = false,
-        super._internal(context);
+        super._internal(abstractValueDomain, context);
 
-  ParameterTypeInformation.static(
+  ParameterTypeInformation.static(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this._parameter, this._type, this._method,
       {bool isInitializingFormal: false})
       : _isInstanceMemberParameter = false,
         _isClosureParameter = false,
         _isInitializingFormal = isInitializingFormal,
-        super._internal(context);
+        super._internal(abstractValueDomain, context);
 
   ParameterTypeInformation.instanceMember(
+      AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
       this._parameter,
       this._type,
@@ -698,7 +719,7 @@
       : _isInstanceMemberParameter = true,
         _isClosureParameter = false,
         _isInitializingFormal = false,
-        super._withAssignments(context, assignments);
+        super._withAssignments(abstractValueDomain, context, assignments);
 
   FunctionEntity get method => _method;
 
@@ -719,7 +740,7 @@
   }
 
   // TODO(herhut): Cleanup into one conditional.
-  TypeMask handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
     if (!inferrer.canFunctionParametersBeUsedForGlobalOptimizations(_method) ||
         inferrer.assumeDynamic(_method)) {
       // Do not infer types for parameters that have a corresponding annotation
@@ -767,7 +788,8 @@
     return null;
   }
 
-  TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+  AbstractValue potentiallyNarrowType(
+      AbstractValue mask, InferrerEngine inferrer) {
     if (inferrer.options.parameterCheckPolicy.isTrusted ||
         inferrer.trustTypeAnnotations(_method)) {
       // In checked or strong mode we don't trust the types of the arguments
@@ -804,14 +826,14 @@
     return mask;
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
-    TypeMask special = handleSpecialCases(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) {
+    AbstractValue special = handleSpecialCases(inferrer);
     if (special != null) return special;
     return potentiallyNarrowType(
         inferrer.types.computeTypeMask(assignments), inferrer);
   }
 
-  TypeMask safeType(InferrerEngine inferrer) {
+  AbstractValue safeType(InferrerEngine inferrer) {
     return potentiallyNarrowType(super.safeType(inferrer), inferrer);
   }
 
@@ -866,13 +888,20 @@
   final Object _call;
   final MemberEntity caller;
   final Selector selector;
-  final TypeMask mask;
+  final AbstractValue mask;
   final ArgumentsTypes arguments;
   final bool inLoop;
 
-  CallSiteTypeInformation(MemberTypeInformation context, this._call,
-      this.caller, this.selector, this.mask, this.arguments, this.inLoop)
-      : super.noAssignments(context) {
+  CallSiteTypeInformation(
+      AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context,
+      this._call,
+      this.caller,
+      this.selector,
+      this.mask,
+      this.arguments,
+      this.inLoop)
+      : super.noAssignments(abstractValueDomain.emptyType, context) {
     assert(_call is ir.Node);
   }
 
@@ -891,15 +920,17 @@
   final MemberEntity calledElement;
 
   StaticCallSiteTypeInformation(
+      AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
       Object call,
       MemberEntity enclosing,
       this.calledElement,
       Selector selector,
-      TypeMask mask,
+      AbstractValue mask,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(context, call, enclosing, selector, mask, arguments, inLoop);
+      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+            arguments, inLoop);
 
   MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
     return inferrer.types.getInferredTypeOfMember(calledElement);
@@ -929,7 +960,7 @@
     return inferrer.typeOfMemberWithSelector(calledElement, selector);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
     if (isSynthesized) {
       assert(arguments != null);
       return _getCalledTypeInfo(inferrer).type;
@@ -971,23 +1002,25 @@
   Iterable<MemberEntity> _concreteTargets;
 
   DynamicCallSiteTypeInformation(
+      AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
       this._callType,
       T call,
       MemberEntity enclosing,
       Selector selector,
-      TypeMask mask,
+      AbstractValue mask,
       this.receiver,
       ArgumentsTypes arguments,
       bool inLoop,
       this.isConditional)
-      : super(context, call, enclosing, selector, mask, arguments, inLoop) {
+      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+            arguments, inLoop) {
     assert(validCallType(_callType, _call));
   }
 
   void addToGraph(InferrerEngine inferrer) {
     assert(receiver != null);
-    TypeMask typeMask = computeTypedSelector(inferrer);
+    AbstractValue typeMask = computeTypedSelector(inferrer);
     _hasClosureCallTargets =
         inferrer.closedWorld.includesClosureCall(selector, typeMask);
     _concreteTargets = inferrer.closedWorld.locateMembers(selector, typeMask);
@@ -1016,8 +1049,8 @@
 
   Iterable<MemberEntity> get callees => _concreteTargets;
 
-  TypeMask computeTypedSelector(InferrerEngine inferrer) {
-    TypeMask receiverType = receiver.type;
+  AbstractValue computeTypedSelector(InferrerEngine inferrer) {
+    AbstractValue receiverType = receiver.type;
 
     if (mask != receiverType) {
       return receiverType == inferrer.abstractValueDomain.dynamicType
@@ -1047,10 +1080,10 @@
    * code.
    */
   TypeInformation handleIntrisifiedSelector(
-      Selector selector, TypeMask mask, InferrerEngine inferrer) {
+      Selector selector, AbstractValue mask, InferrerEngine inferrer) {
     ClosedWorld closedWorld = inferrer.closedWorld;
     if (mask == null) return null;
-    if (!mask.containsOnlyInt(closedWorld)) {
+    if (!inferrer.abstractValueDomain.isIntegerOrNull(mask)) {
       return null;
     }
     if (!selector.isCall && !selector.isOperator) return null;
@@ -1143,26 +1176,28 @@
     }
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
+    ClosedWorld closedWorld = inferrer.closedWorld;
+    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
     Iterable<MemberEntity> oldTargets = _concreteTargets;
-    TypeMask typeMask = computeTypedSelector(inferrer);
+    AbstractValue typeMask = computeTypedSelector(inferrer);
     inferrer.updateSelectorInMember(
         caller, _callType, _call, selector, typeMask);
 
-    TypeMask maskToUse =
-        inferrer.closedWorld.extendMaskIfReachesAll(selector, typeMask);
-    bool canReachAll = inferrer.closedWorld.backendUsage.isInvokeOnUsed &&
-        (maskToUse != typeMask);
+    AbstractValue maskToUse =
+        closedWorld.extendMaskIfReachesAll(selector, typeMask);
+    bool canReachAll =
+        closedWorld.backendUsage.isInvokeOnUsed && (maskToUse != typeMask);
 
     // If this call could potentially reach all methods that satisfy
     // the untyped selector (through noSuchMethod's `Invocation`
     // and a call to `delegate`), we iterate over all these methods to
     // update their parameter types.
     _hasClosureCallTargets =
-        inferrer.closedWorld.includesClosureCall(selector, maskToUse);
-    _concreteTargets = inferrer.closedWorld.locateMembers(selector, maskToUse);
+        closedWorld.includesClosureCall(selector, maskToUse);
+    _concreteTargets = closedWorld.locateMembers(selector, maskToUse);
     Iterable<MemberEntity> typedTargets = canReachAll
-        ? inferrer.closedWorld.locateMembers(selector, typeMask)
+        ? closedWorld.locateMembers(selector, typeMask)
         : _concreteTargets;
 
     // Update the call graph if the targets could have changed.
@@ -1196,9 +1231,9 @@
 
     // Walk over the found targets, and compute the joined union type mask
     // for all these targets.
-    TypeMask result;
+    AbstractValue result;
     if (_hasClosureCallTargets) {
-      result = inferrer.abstractValueDomain.dynamicType;
+      result = abstractValueDomain.dynamicType;
     } else {
       result = inferrer.types
           .joinTypeMasks(_concreteTargets.map((MemberEntity element) {
@@ -1208,24 +1243,24 @@
         // `Invocation.delegate`. Note that the `noSuchMethod` targets
         // are included in [typedTargets].
         if (canReachAll && !typedTargets.contains(element)) {
-          return const TypeMask.nonNullEmpty();
+          return abstractValueDomain.emptyType;
         }
         if (inferrer.returnsListElementType(selector, typeMask)) {
-          ContainerTypeMask containerTypeMask = receiver.type;
-          return containerTypeMask.elementType;
+          return abstractValueDomain.getContainerElementType(receiver.type);
         } else if (inferrer.returnsMapValueType(selector, typeMask)) {
-          if (typeMask.isDictionary) {
-            TypeMask arg = arguments.positional[0].type;
-            if (arg is ValueTypeMask && arg.value.isString) {
-              DictionaryTypeMask dictionaryTypeMask = typeMask;
-              StringConstantValue value = arg.value;
+          if (abstractValueDomain.isDictionary(typeMask)) {
+            AbstractValue arg = arguments.positional[0].type;
+            ConstantValue value = abstractValueDomain.getPrimitiveValue(arg);
+            if (value is StringConstantValue) {
               String key = value.stringValue;
-              if (dictionaryTypeMask.typeMap.containsKey(key)) {
+              if (abstractValueDomain.containsDictionaryKey(typeMask, key)) {
                 if (debug.VERBOSE) {
                   print("Dictionary lookup for $key yields "
-                      "${dictionaryTypeMask.typeMap[key]}.");
+                      "${abstractValueDomain.
+                      getDictionaryValueForKey(typeMask, key)}.");
                 }
-                return dictionaryTypeMask.typeMap[key];
+                return abstractValueDomain.getDictionaryValueForKey(
+                    typeMask, key);
               } else {
                 // The typeMap is precise, so if we do not find the key, the lookup
                 // will be [null] at runtime.
@@ -1236,11 +1271,12 @@
               }
             }
           }
-          MapTypeMask mapTypeMask = typeMask;
+          assert(abstractValueDomain.isMap(typeMask));
           if (debug.VERBOSE) {
-            print("Map lookup for $selector yields ${mapTypeMask.valueType}.");
+            print("Map lookup for $selector yields "
+                "${abstractValueDomain.getMapValueType(typeMask)}.");
           }
-          return mapTypeMask.valueType;
+          return abstractValueDomain.getMapValueType(typeMask);
         } else {
           TypeInformation info =
               handleIntrisifiedSelector(selector, typeMask, inferrer);
@@ -1249,10 +1285,10 @@
         }
       }));
     }
-    if (isConditional && receiver.type.isNullable) {
+    if (isConditional && abstractValueDomain.canBeNull(receiver.type)) {
       // Conditional call sites (e.g. `a?.b`) may be null if the receiver is
       // null.
-      result = result.nullable();
+      result = abstractValueDomain.includeNull(result);
     }
     return result;
   }
@@ -1309,22 +1345,24 @@
   final TypeInformation closure;
 
   ClosureCallSiteTypeInformation(
+      AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
       Object call,
       MemberEntity enclosing,
       Selector selector,
-      TypeMask mask,
+      AbstractValue mask,
       this.closure,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(context, call, enclosing, selector, mask, arguments, inLoop);
+      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+            arguments, inLoop);
 
   void addToGraph(InferrerEngine inferrer) {
     arguments.forEach((info) => info.addUser(this));
     closure.addUser(this);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
 
   Iterable<MemberEntity> get callees {
     throw new UnsupportedError("Cannot compute callees of a closure call.");
@@ -1358,8 +1396,7 @@
  * type.
  */
 class ConcreteTypeInformation extends TypeInformation {
-  ConcreteTypeInformation(TypeMask type) : super.untracked() {
-    this.type = type;
+  ConcreteTypeInformation(AbstractValue type) : super.untracked(type) {
     this.isStable = true;
   }
 
@@ -1385,7 +1422,7 @@
     throw "Not supported";
   }
 
-  TypeMask computeType(InferrerEngine inferrer) => type;
+  AbstractValue computeType(InferrerEngine inferrer) => type;
 
   bool reset(InferrerEngine inferrer) {
     throw "Not supported";
@@ -1403,8 +1440,10 @@
 class StringLiteralTypeInformation extends ConcreteTypeInformation {
   final String value;
 
-  StringLiteralTypeInformation(this.value, TypeMask mask)
-      : super(new ValueTypeMask(mask, new StringConstantValue(value)));
+  StringLiteralTypeInformation(
+      AbstractValueDomain abstractValueDomain, this.value, AbstractValue mask)
+      : super(abstractValueDomain.createPrimitiveValue(
+            mask, new StringConstantValue(value)));
 
   String asString() => value;
   String toString() => 'Type $type value ${value}';
@@ -1417,8 +1456,9 @@
 class BoolLiteralTypeInformation extends ConcreteTypeInformation {
   final bool value;
 
-  BoolLiteralTypeInformation(this.value, TypeMask mask)
-      : super(new ValueTypeMask(
+  BoolLiteralTypeInformation(
+      AbstractValueDomain abstractValueDomain, this.value, AbstractValue mask)
+      : super(abstractValueDomain.createPrimitiveValue(
             mask, value ? new TrueConstantValue() : new FalseConstantValue()));
 
   String toString() => 'Type $type value ${value}';
@@ -1448,10 +1488,11 @@
  *   information on the type of a local.
  */
 class NarrowTypeInformation extends TypeInformation {
-  final TypeMask typeAnnotation;
+  final AbstractValue typeAnnotation;
 
-  NarrowTypeInformation(TypeInformation narrowedType, this.typeAnnotation)
-      : super(narrowedType.context) {
+  NarrowTypeInformation(AbstractValueDomain abstractValueDomain,
+      TypeInformation narrowedType, this.typeAnnotation)
+      : super(abstractValueDomain.emptyType, narrowedType.context) {
     addAssignment(narrowedType);
   }
 
@@ -1460,13 +1501,14 @@
     assert(assignments.length == 1);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
-    TypeMask input = assignments.first.type;
-    TypeMask intersection =
-        input.intersection(typeAnnotation, inferrer.closedWorld);
+  AbstractValue computeType(InferrerEngine inferrer) {
+    AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
+    AbstractValue input = assignments.first.type;
+    AbstractValue intersection =
+        abstractValueDomain.intersection(input, typeAnnotation);
     if (debug.ANOMALY_WARN) {
-      if (!input.containsMask(intersection, inferrer.closedWorld) ||
-          !typeAnnotation.containsMask(intersection, inferrer.closedWorld)) {
+      if (!abstractValueDomain.contains(input, intersection) ||
+          !abstractValueDomain.contains(typeAnnotation, intersection)) {
         print("ANOMALY WARNING: narrowed $input to $intersection via "
             "$typeAnnotation");
       }
@@ -1493,13 +1535,13 @@
   /** Whether the element type in that container has been inferred. */
   bool inferred = false;
 
-  InferredTypeInformation(
+  InferredTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, TypeInformation parentType)
-      : super(context) {
+      : super(abstractValueDomain.emptyType, context) {
     if (parentType != null) addAssignment(parentType);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
     if (!inferred) return safeType(inferrer);
     return inferrer.types.computeTypeMask(assignments);
   }
@@ -1517,7 +1559,7 @@
   final ElementInContainerTypeInformation elementType;
 
   /** The container type before it is inferred. */
-  final ContainerTypeMask originalType;
+  final AbstractValue originalType;
 
   /** The length at the allocation site. */
   final int originalLength;
@@ -1531,11 +1573,14 @@
    */
   bool checksGrowable = true;
 
-  ListTypeInformation(MemberTypeInformation context, this.originalType,
-      this.elementType, this.originalLength)
-      : super(context) {
-    type = originalType;
-    inferredLength = originalType.length;
+  ListTypeInformation(
+      AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context,
+      this.originalType,
+      this.elementType,
+      this.originalLength)
+      : super(originalType, context) {
+    inferredLength = abstractValueDomain.getContainerLength(originalType);
     elementType.addUser(this);
   }
 
@@ -1549,22 +1594,23 @@
     return elementType.isStable && super.hasStableType(inferrer);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
-    dynamic mask = type;
-    if (!mask.isContainer ||
-        mask.elementType != elementType.type ||
-        mask.length != inferredLength) {
-      return new ContainerTypeMask(
-          originalType.forwardTo,
-          originalType.allocationNode,
-          originalType.allocationElement,
+  AbstractValue computeType(InferrerEngine inferrer) {
+    AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
+    AbstractValue mask = type;
+    if (!abstractValueDomain.isContainer(type) ||
+        abstractValueDomain.getContainerElementType(type) != elementType.type ||
+        abstractValueDomain.getContainerLength(type) != inferredLength) {
+      return abstractValueDomain.createContainerValue(
+          abstractValueDomain.getGeneralization(originalType),
+          abstractValueDomain.getAllocationNode(originalType),
+          abstractValueDomain.getAllocationElement(originalType),
           elementType.type,
           inferredLength);
     }
     return mask;
   }
 
-  TypeMask safeType(InferrerEngine inferrer) => originalType;
+  AbstractValue safeType(InferrerEngine inferrer) => originalType;
 
   void cleanup() {
     super.cleanup();
@@ -1578,8 +1624,9 @@
  * elements in a [ListTypeInformation].
  */
 class ElementInContainerTypeInformation extends InferredTypeInformation {
-  ElementInContainerTypeInformation(MemberTypeInformation context, elementType)
-      : super(context, elementType);
+  ElementInContainerTypeInformation(AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context, elementType)
+      : super(abstractValueDomain, context, elementType);
 
   String toString() => 'Element in container $type';
 
@@ -1599,7 +1646,7 @@
   // These fields track the overall type of the keys/values in the map.
   final KeyInMapTypeInformation keyType;
   final ValueInMapTypeInformation valueType;
-  final MapTypeMask originalType;
+  final AbstractValue originalType;
 
   // Set to false if a statically unknown key flows into this map.
   bool _allKeysAreStrings = true;
@@ -1608,19 +1655,20 @@
 
   MapTypeInformation(MemberTypeInformation context, this.originalType,
       this.keyType, this.valueType)
-      : super(context) {
+      : super(originalType, context) {
     keyType.addUser(this);
     valueType.addUser(this);
-    type = originalType;
   }
 
-  TypeInformation addEntryAssignment(TypeInformation key, TypeInformation value,
+  TypeInformation addEntryAssignment(AbstractValueDomain abstractValueDomain,
+      TypeInformation key, TypeInformation value,
       [bool nonNull = false]) {
     TypeInformation newInfo = null;
     if (_allKeysAreStrings && key is StringLiteralTypeInformation) {
       String keyString = key.asString();
       typeInfoMap.putIfAbsent(keyString, () {
-        newInfo = new ValueInMapTypeInformation(context, null, nonNull);
+        newInfo = new ValueInMapTypeInformation(
+            abstractValueDomain, context, null, nonNull);
         return newInfo;
       });
       typeInfoMap[keyString].addAssignment(value);
@@ -1635,13 +1683,14 @@
     return newInfo;
   }
 
-  List<TypeInformation> addMapAssignment(MapTypeInformation other) {
+  List<TypeInformation> addMapAssignment(
+      AbstractValueDomain abstractValueDomain, MapTypeInformation other) {
     List<TypeInformation> newInfos = <TypeInformation>[];
     if (_allKeysAreStrings && other.inDictionaryMode) {
       other.typeInfoMap.forEach((keyString, value) {
         typeInfoMap.putIfAbsent(keyString, () {
-          TypeInformation newInfo =
-              new ValueInMapTypeInformation(context, null, false);
+          TypeInformation newInfo = new ValueInMapTypeInformation(
+              abstractValueDomain, context, null, false);
           newInfos.add(newInfo);
           return newInfo;
         });
@@ -1670,49 +1719,51 @@
     return visitor.visitMapTypeInformation(this);
   }
 
-  TypeMask toTypeMask(InferrerEngine inferrer) {
+  AbstractValue toTypeMask(InferrerEngine inferrer) {
+    AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
     if (inDictionaryMode) {
-      Map<String, TypeMask> mappings = new Map<String, TypeMask>();
+      Map<String, AbstractValue> mappings = new Map<String, AbstractValue>();
       for (var key in typeInfoMap.keys) {
         mappings[key] = typeInfoMap[key].type;
       }
-      return new DictionaryTypeMask(
-          originalType.forwardTo,
-          originalType.allocationNode,
-          originalType.allocationElement,
+      return inferrer.abstractValueDomain.createDictionaryValue(
+          abstractValueDomain.getGeneralization(originalType),
+          abstractValueDomain.getAllocationNode(originalType),
+          abstractValueDomain.getAllocationElement(originalType),
           keyType.type,
           valueType.type,
           mappings);
     } else {
-      return new MapTypeMask(
-          originalType.forwardTo,
-          originalType.allocationNode,
-          originalType.allocationElement,
+      return inferrer.abstractValueDomain.createMapValue(
+          abstractValueDomain.getGeneralization(originalType),
+          abstractValueDomain.getAllocationNode(originalType),
+          abstractValueDomain.getAllocationElement(originalType),
           keyType.type,
           valueType.type);
     }
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
-    if (type.isDictionary != inDictionaryMode) {
+  AbstractValue computeType(InferrerEngine inferrer) {
+    AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
+    if (abstractValueDomain.isDictionary(type) != inDictionaryMode) {
       return toTypeMask(inferrer);
-    } else if (type.isDictionary) {
+    } else if (abstractValueDomain.isDictionary(type)) {
       assert(inDictionaryMode);
-      DictionaryTypeMask mask = type;
-      for (var key in typeInfoMap.keys) {
+      for (String key in typeInfoMap.keys) {
         TypeInformation value = typeInfoMap[key];
-        if (!mask.typeMap.containsKey(key) &&
-            !value.type.containsAll(inferrer.closedWorld) &&
-            !value.type.isNullable) {
+        if (!abstractValueDomain.containsDictionaryKey(type, key) &&
+            !abstractValueDomain.containsAll(value.type) &&
+            !abstractValueDomain.canBeNull(value.type)) {
           return toTypeMask(inferrer);
         }
-        if (mask.typeMap[key] != typeInfoMap[key].type) {
+        if (abstractValueDomain.getDictionaryValueForKey(type, key) !=
+            typeInfoMap[key].type) {
           return toTypeMask(inferrer);
         }
       }
-    } else if (type.isMap) {
-      MapTypeMask mask = type;
-      if (mask.keyType != keyType.type || mask.valueType != valueType.type) {
+    } else if (abstractValueDomain.isMap(type)) {
+      if (abstractValueDomain.getMapKeyType(type) != keyType.type ||
+          abstractValueDomain.getMapValueType(type) != valueType.type) {
         return toTypeMask(inferrer);
       }
     } else {
@@ -1722,7 +1773,7 @@
     return type;
   }
 
-  TypeMask safeType(InferrerEngine inferrer) => originalType;
+  AbstractValue safeType(InferrerEngine inferrer) => originalType;
 
   bool hasStableType(InferrerEngine inferrer) {
     return keyType.isStable &&
@@ -1750,9 +1801,9 @@
  * for the keys in a [MapTypeInformation]
  */
 class KeyInMapTypeInformation extends InferredTypeInformation {
-  KeyInMapTypeInformation(
+  KeyInMapTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, TypeInformation keyType)
-      : super(context, keyType);
+      : super(abstractValueDomain, context, keyType);
 
   accept(TypeInformationVisitor visitor) {
     return visitor.visitKeyInMapTypeInformation(this);
@@ -1771,19 +1822,19 @@
   // mode can ever be marked as [nonNull].
   final bool nonNull;
 
-  ValueInMapTypeInformation(
+  ValueInMapTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, TypeInformation valueType,
       [this.nonNull = false])
-      : super(context, valueType);
+      : super(abstractValueDomain, context, valueType);
 
   accept(TypeInformationVisitor visitor) {
     return visitor.visitValueInMapTypeInformation(this);
   }
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
     return nonNull
         ? super.computeType(inferrer)
-        : super.computeType(inferrer).nullable();
+        : inferrer.abstractValueDomain.includeNull(super.computeType(inferrer));
   }
 
   String toString() => 'Value in Map $type';
@@ -1798,12 +1849,12 @@
   final Local variable;
   final bool isTry;
 
-  PhiElementTypeInformation(
+  PhiElementTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this.branchNode, this.variable,
       {this.isTry})
-      : super(context);
+      : super(abstractValueDomain.emptyType, context);
 
-  TypeMask computeType(InferrerEngine inferrer) {
+  AbstractValue computeType(InferrerEngine inferrer) {
     return inferrer.types.computeTypeMask(assignments);
   }
 
@@ -1835,14 +1886,15 @@
     with ApplyableTypeInformation {
   final FunctionEntity _element;
 
-  ClosureTypeInformation(MemberTypeInformation context, this._element)
-      : super(context);
+  ClosureTypeInformation(AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context, this._element)
+      : super(abstractValueDomain.emptyType, context);
 
   FunctionEntity get closure => _element;
 
-  TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
 
-  TypeMask safeType(InferrerEngine inferrer) {
+  AbstractValue safeType(InferrerEngine inferrer) {
     return inferrer.types.functionType.type;
   }
 
@@ -1900,11 +1952,12 @@
 class AwaitTypeInformation<T> extends TypeInformation {
   final T _node;
 
-  AwaitTypeInformation(MemberTypeInformation context, this._node)
-      : super(context);
+  AwaitTypeInformation(AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context, this._node)
+      : super(abstractValueDomain.emptyType, context);
 
   // TODO(22894): Compute a better type here.
-  TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
 
   String get debugName => '$_node';
 
@@ -1918,10 +1971,11 @@
 class YieldTypeInformation<T> extends TypeInformation {
   final T _node;
 
-  YieldTypeInformation(MemberTypeInformation context, this._node)
-      : super(context);
+  YieldTypeInformation(AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context, this._node)
+      : super(abstractValueDomain.emptyType, context);
 
-  TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer);
+  AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
 
   String get debugName => '$_node';
 
@@ -1954,10 +2008,11 @@
   T visitYieldTypeInformation(YieldTypeInformation info);
 }
 
-TypeMask _narrowType(
-    ClosedWorld closedWorld, TypeMask type, DartType annotation,
+AbstractValue _narrowType(
+    ClosedWorld closedWorld, AbstractValue type, DartType annotation,
     {bool isNullable: true}) {
-  TypeMask otherType;
+  AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+  AbstractValue otherType;
   if (annotation.treatAsDynamic) {
     return type;
   } else if (annotation.isInterfaceType) {
@@ -1965,7 +2020,7 @@
     if (interfaceType.element == closedWorld.commonElements.objectClass) {
       return type;
     }
-    otherType = new TypeMask.nonNullSubtype(interfaceType.element, closedWorld);
+    otherType = abstractValueDomain.createNonNullSubtype(interfaceType.element);
   } else if (annotation.isVoid) {
     return type;
   } else if (annotation.isTypedef || annotation.isFunctionType) {
@@ -1978,7 +2033,11 @@
     // TODO(ngeoffray): Narrow to bound.
     return type;
   }
-  if (isNullable) otherType = otherType.nullable();
-  if (type == null) return otherType;
-  return type.intersection(otherType, closedWorld);
+  if (isNullable) {
+    otherType = abstractValueDomain.includeNull(otherType);
+  }
+  if (type == null) {
+    return otherType;
+  }
+  return abstractValueDomain.intersection(type, otherType);
 }
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index eb3bc06..672ebbe 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -6,7 +6,6 @@
 import '../elements/entities.dart';
 import '../elements/types.dart';
 import '../types/abstract_value_domain.dart';
-import '../types/masks.dart' show ContainerTypeMask, MapTypeMask;
 import '../universe/selector.dart';
 import '../world.dart';
 import 'type_graph_nodes.dart';
@@ -15,11 +14,14 @@
 /// information for nodes.
 abstract class TypeSystemStrategy<T> {
   /// Creates [MemberTypeInformation] for [member].
-  MemberTypeInformation createMemberTypeInformation(MemberEntity member);
+  MemberTypeInformation createMemberTypeInformation(
+      AbstractValueDomain abstractValueDomain, MemberEntity member);
 
   /// Creates [ParameterTypeInformation] for [parameter].
   ParameterTypeInformation createParameterTypeInformation(
-      Local parameter, TypeSystem<T> types);
+      AbstractValueDomain abstractValueDomain,
+      Local parameter,
+      TypeSystem<T> types);
 
   /// Calls [f] for each parameter in [function].
   void forEachParameter(FunctionEntity function, void f(Local parameter));
@@ -261,11 +263,12 @@
 
   TypeInformation stringLiteralType(String value) {
     return new StringLiteralTypeInformation(
-        value, _abstractValueDomain.stringType);
+        _abstractValueDomain, value, _abstractValueDomain.stringType);
   }
 
   TypeInformation boolLiteralType(bool value) {
-    return new BoolLiteralTypeInformation(value, _abstractValueDomain.boolType);
+    return new BoolLiteralTypeInformation(
+        _abstractValueDomain, value, _abstractValueDomain.boolType);
   }
 
   /**
@@ -282,7 +285,7 @@
       return dynamicType;
     }
     return getConcreteTypeFor(
-        firstType.type.union(secondType.type, _closedWorld));
+        _abstractValueDomain.union(firstType.type, secondType.type));
   }
 
   /**
@@ -304,7 +307,7 @@
   TypeInformation refineReceiver(
       Selector selector, AbstractValue mask, TypeInformation receiver,
       {bool isConditional}) {
-    if (receiver.type.isExact) return receiver;
+    if (_abstractValueDomain.isExact(receiver.type)) return receiver;
     AbstractValue otherType = _closedWorld.computeReceiverType(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.
@@ -319,7 +322,8 @@
         _abstractValueDomain.containsAll(otherType)) {
       return receiver;
     }
-    TypeInformation newType = new NarrowTypeInformation(receiver, otherType);
+    TypeInformation newType =
+        new NarrowTypeInformation(_abstractValueDomain, receiver, otherType);
     allocatedTypes.add(newType);
     return newType;
   }
@@ -340,7 +344,7 @@
         if (isNullable) {
           return type;
         }
-        otherType = dynamicType.type.nonNullable();
+        otherType = _abstractValueDomain.excludeNull(dynamicType.type);
       } else {
         otherType =
             _abstractValueDomain.createNonNullSubtype(interface.element);
@@ -358,10 +362,11 @@
     if (isNullable) {
       otherType = _abstractValueDomain.includeNull(otherType);
     }
-    if (type.type.isExact) {
+    if (_abstractValueDomain.isExact(type.type)) {
       return type;
     } else {
-      TypeInformation newType = new NarrowTypeInformation(type, otherType);
+      TypeInformation newType =
+          new NarrowTypeInformation(_abstractValueDomain, type, otherType);
       allocatedTypes.add(newType);
       return newType;
     }
@@ -371,11 +376,11 @@
    * Returns the non-nullable type of [type].
    */
   TypeInformation narrowNotNull(TypeInformation type) {
-    if (type.type.isExact && !type.type.isNullable) {
+    if (_abstractValueDomain.isExact(type.type)) {
       return type;
     }
-    TypeInformation newType =
-        new NarrowTypeInformation(type, dynamicType.type.nonNullable());
+    TypeInformation newType = new NarrowTypeInformation(_abstractValueDomain,
+        type, _abstractValueDomain.excludeNull(dynamicType.type));
     allocatedTypes.add(newType);
     return newType;
   }
@@ -383,7 +388,8 @@
   ParameterTypeInformation getInferredTypeOfParameter(Local parameter) {
     return parameterTypeInformations.putIfAbsent(parameter, () {
       ParameterTypeInformation typeInformation =
-          strategy.createParameterTypeInformation(parameter, this);
+          strategy.createParameterTypeInformation(
+              _abstractValueDomain, parameter, this);
       _orderedTypeInformations.add(typeInformation);
       return typeInformation;
     });
@@ -394,7 +400,7 @@
         failedAt(member, "Unexpected abstract member $member."));
     return memberTypeInformations.putIfAbsent(member, () {
       MemberTypeInformation typeInformation =
-          strategy.createMemberTypeInformation(member);
+          strategy.createMemberTypeInformation(_abstractValueDomain, member);
       _orderedTypeInformations.add(typeInformation);
       return typeInformation;
     });
@@ -451,7 +457,7 @@
     ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
     bool isTypedArray = typedDataClass != null &&
         _closedWorld.isInstantiated(typedDataClass) &&
-        type.type.satisfies(typedDataClass, _closedWorld);
+        _abstractValueDomain.isInstanceOfOrNull(type.type, typedDataClass);
     bool isConst = (type.type == _abstractValueDomain.constListType);
     bool isFixed = (type.type == _abstractValueDomain.fixedListType) ||
         isConst ||
@@ -461,22 +467,24 @@
     int inferredLength = isFixed ? length : null;
     AbstractValue elementTypeMask =
         isElementInferred ? elementType.type : dynamicType.type;
-    ContainerTypeMask<T> mask = new ContainerTypeMask<T>(
+    AbstractValue mask = _abstractValueDomain.createContainerValue(
         type.type, node, enclosing, elementTypeMask, inferredLength);
     ElementInContainerTypeInformation element =
-        new ElementInContainerTypeInformation(currentMember, elementType);
+        new ElementInContainerTypeInformation(
+            _abstractValueDomain, currentMember, elementType);
     element.inferred = isElementInferred;
 
     allocatedTypes.add(element);
-    return allocatedLists[node] =
-        new ListTypeInformation(currentMember, mask, element, length);
+    return allocatedLists[node] = new ListTypeInformation(
+        _abstractValueDomain, currentMember, mask, element, length);
   }
 
   /// Creates a [TypeInformation] object either for the closurization of a
   /// static or top-level method [element] used as a function constant or for
   /// the synthesized 'call' method [element] created for a local function.
   TypeInformation allocateClosure(FunctionEntity element) {
-    TypeInformation result = new ClosureTypeInformation(currentMember, element);
+    TypeInformation result = new ClosureTypeInformation(
+        _abstractValueDomain, currentMember, element);
     allocatedClosures.add(result);
     return result;
   }
@@ -497,13 +505,13 @@
     } else {
       keyType = valueType = dynamicType.type;
     }
-    MapTypeMask mask =
-        new MapTypeMask(type.type, node, element, keyType, valueType);
+    AbstractValue mask = _abstractValueDomain.createMapValue(
+        type.type, node, element, keyType, valueType);
 
     TypeInformation keyTypeInfo =
-        new KeyInMapTypeInformation(currentMember, null);
-    TypeInformation valueTypeInfo =
-        new ValueInMapTypeInformation(currentMember, null);
+        new KeyInMapTypeInformation(_abstractValueDomain, currentMember, null);
+    TypeInformation valueTypeInfo = new ValueInMapTypeInformation(
+        _abstractValueDomain, currentMember, null);
     allocatedTypes.add(keyTypeInfo);
     allocatedTypes.add(valueTypeInfo);
 
@@ -511,8 +519,8 @@
         new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
 
     for (int i = 0; i < keyTypes.length; ++i) {
-      TypeInformation newType =
-          map.addEntryAssignment(keyTypes[i], valueTypes[i], true);
+      TypeInformation newType = map.addEntryAssignment(
+          _abstractValueDomain, keyTypes[i], valueTypes[i], true);
       if (newType != null) allocatedTypes.add(newType);
     }
 
@@ -537,7 +545,7 @@
   TypeInformation allocateDiamondPhi(
       TypeInformation firstInput, TypeInformation secondInput) {
     PhiElementTypeInformation<T> result = new PhiElementTypeInformation<T>(
-        currentMember, null, null,
+        _abstractValueDomain, currentMember, null, null,
         isTry: false);
     result.addAssignment(firstInput);
     result.addAssignment(secondInput);
@@ -548,7 +556,7 @@
   PhiElementTypeInformation<T> _addPhi(
       T node, Local variable, TypeInformation inputType, bool isTry) {
     PhiElementTypeInformation<T> result = new PhiElementTypeInformation<T>(
-        currentMember, node, variable,
+        _abstractValueDomain, currentMember, node, variable,
         isTry: isTry);
     allocatedTypes.add(result);
     result.addAssignment(inputType);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 67bd1e0..c9e3647 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1010,7 +1010,8 @@
   /// Does this node potentially affect control flow.
   bool isControlFlow() => false;
 
-  bool isExact(AbstractValueDomain domain) => domain.isExact(instructionType);
+  bool isExact(AbstractValueDomain domain) =>
+      domain.isExactOrNull(instructionType);
 
   bool isValue(AbstractValueDomain domain) =>
       domain.isPrimitiveValue(instructionType);
diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart
index 95c7a90..2534ec8 100644
--- a/pkg/compiler/lib/src/types/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart
@@ -4,7 +4,7 @@
 
 library dart2js.abstract_value_domain;
 
-import '../constants/values.dart' show ConstantValue;
+import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
 import '../elements/entities.dart';
 import '../universe/selector.dart';
 
@@ -155,16 +155,16 @@
   /// Returns `true` if [value] is empty set of runtime values.
   bool isEmpty(covariant AbstractValue value);
 
-  /// Returns `true` if [value] is an exact class or `null` at runtime.
+  /// Returns `true` if [value] is a non-null exact class at runtime.
   bool isExact(covariant AbstractValue value);
 
+  /// Returns `true` if [value] is an exact class or `null` at runtime.
+  bool isExactOrNull(covariant AbstractValue value);
+
   /// Returns the [ClassEntity] if this [value] is a non-null instance of an
   /// exact class at runtime, and `null` otherwise.
   ClassEntity getExactClass(covariant AbstractValue value);
 
-  /// Returns `true` if [value] a known primitive JavaScript value at runtime.
-  bool isPrimitiveValue(covariant AbstractValue value);
-
   /// Returns `true` if [value] can be `null` at runtime.
   bool canBeNull(covariant AbstractValue value);
 
@@ -292,21 +292,116 @@
   /// Returns `true` if [value] represents a container value at runtime.
   bool isContainer(covariant AbstractValue value);
 
+  /// Creates a container value specialization of [originalValue] with the
+  /// inferred [element] runtime value and inferred runtime [length].
+  ///
+  /// The [allocationNode] is used to identify this particular map allocation.
+  /// The [allocationElement] is used only for debugging.
+  AbstractValue createContainerValue(
+      AbstractValue originalValue,
+      Object allocationNode,
+      MemberEntity allocationElement,
+      AbstractValue elementType,
+      int length);
+
   /// Returns the element type of [value] if it represents a container value
   /// at runtime. Returns [dynamicType] otherwise.
   AbstractValue getContainerElementType(AbstractValue value);
 
+  /// Return the known length of [value] if it represents a container value
+  /// at runtime. Returns `null` if the length is unknown or if [value] doesn't
+  /// represent a container value at runtime.
+  int getContainerLength(AbstractValue value);
+
   /// Returns `true` if [value] represents a map value at runtime.
   bool isMap(covariant AbstractValue value);
 
+  /// Creates a map value specialization of [originalValue] with the inferred
+  /// [key] and [value] runtime values.
+  ///
+  /// The [allocationNode] is used to identify this particular map allocation.
+  /// The [allocationElement] is used only for debugging.
+  AbstractValue createMapValue(
+      AbstractValue originalValue,
+      Object allocationNode,
+      MemberEntity allocationElement,
+      AbstractValue key,
+      AbstractValue value);
+
+  /// Returns the key type of [value] if it represents a map value at runtime.
+  /// Returns [dynamicType] otherwise.
+  AbstractValue getMapKeyType(AbstractValue value);
+
   /// Returns the value type of [value] if it represents a map value at runtime.
   /// Returns [dynamicType] otherwise.
   AbstractValue getMapValueType(AbstractValue value);
 
+  /// Returns `true` if [value] represents a dictionary value, that is, a map
+  /// with strings as keys, at runtime.
+  bool isDictionary(covariant AbstractValue value);
+
+  /// Creates a dictionary value specialization of [originalValue] with the
+  /// inferred [key] and [value] runtime values.
+  ///
+  /// The [allocationNode] is used to identify this particular map allocation.
+  /// The [allocationElement] is used only for debugging.
+  AbstractValue createDictionaryValue(
+      AbstractValue originalValue,
+      Object allocationNode,
+      MemberEntity allocationElement,
+      AbstractValue key,
+      AbstractValue value,
+      Map<String, AbstractValue> mappings);
+
+  /// Returns `true` if [value] is a dictionary value which contains [key] as
+  /// a key.
+  bool containsDictionaryKey(AbstractValue value, String key);
+
+  /// Returns the value type for [key] in [value] if it represents a dictionary
+  /// value at runtime. Returns [dynamicType] otherwise.
+  AbstractValue getDictionaryValueForKey(AbstractValue value, String key);
+
+  /// Returns `true` if [specialization] is a specialization of
+  /// [generalization].
+  ///
+  /// Specializations are created through [createPrimitiveValue],
+  /// [createMapValue], [createDictionaryValue] and [createContainerValue].
+  bool isSpecializationOf(
+      AbstractValue specialization, AbstractValue generalization);
+
+  /// Returns the value of which [value] is a specialization. Return `null` if
+  /// [value] is not a specialization.
+  ///
+  /// Specializations are created through [createPrimitiveValue],
+  /// [createMapValue], [createDictionaryValue] and [createContainerValue].
+  AbstractValue getGeneralization(AbstractValue value);
+
+  /// Return the object identifying the allocation of [value] if it is an
+  /// allocation based specialization. Otherwise returns `null`.
+  ///
+  /// Allocation based specializations are created through [createMapValue],
+  /// [createDictionaryValue] and [createContainerValue]
+  Object getAllocationNode(AbstractValue value);
+
+  /// Return the allocation element of [value] if it is an allocation based
+  /// specialization. Otherwise returns `null`.
+  ///
+  /// Allocation based specializations are created through [createMapValue],
+  /// [createDictionaryValue] and [createContainerValue]
+  MemberEntity getAllocationElement(AbstractValue value);
+
+  /// Returns `true` if [value] a known primitive JavaScript value at runtime.
+  bool isPrimitiveValue(covariant AbstractValue value);
+
+  /// Creates a primitive value specialization of [originalValue] with the
+  /// inferred primitive constant [value].
+  AbstractValue createPrimitiveValue(
+      AbstractValue originalValue, PrimitiveConstantValue value);
+
   /// Returns the primitive JavaScript value of [value] if it represents a
   /// primitive JavaScript value at runtime, value at runtime. Returns `null`
   /// otherwise.
-  ConstantValue getPrimitiveValue(covariant AbstractValue value);
+  PrimitiveConstantValue getPrimitiveValue(covariant AbstractValue value);
 
   /// Compute the type of all potential receivers of the set of live [members].
   AbstractValue computeReceiver(Iterable<MemberEntity> members);
@@ -346,4 +441,7 @@
 
   /// Returns `true` if [value] is an JavaScript indexable of fixed length.
   bool isFixedLengthJsIndexable(AbstractValue value);
+
+  /// Returns compact a textual representation for [value] used for debugging.
+  String getCompactText(AbstractValue value);
 }
diff --git a/pkg/compiler/lib/src/types/container_type_mask.dart b/pkg/compiler/lib/src/types/container_type_mask.dart
index fbbd59f..f389c66 100644
--- a/pkg/compiler/lib/src/types/container_type_mask.dart
+++ b/pkg/compiler/lib/src/types/container_type_mask.dart
@@ -7,7 +7,7 @@
 /// A [ContainerTypeMask] is a [TypeMask] for a specific allocation
 /// site of a container (currently only List) that will get specialized
 /// once the [TypeGraphInferrer] phase finds an element type for it.
-class ContainerTypeMask<T> extends ForwardingTypeMask {
+class ContainerTypeMask<T> extends AllocationTypeMask<T> {
   final TypeMask forwardTo;
 
   // The [Node] where this type mask was created.
diff --git a/pkg/compiler/lib/src/types/dictionary_type_mask.dart b/pkg/compiler/lib/src/types/dictionary_type_mask.dart
index 3d92fcc..3a620c4 100644
--- a/pkg/compiler/lib/src/types/dictionary_type_mask.dart
+++ b/pkg/compiler/lib/src/types/dictionary_type_mask.dart
@@ -14,7 +14,7 @@
  */
 class DictionaryTypeMask<T> extends MapTypeMask<T> {
   // The underlying key/value map of this dictionary.
-  final Map<String, TypeMask> typeMap;
+  final Map<String, AbstractValue> _typeMap;
 
   DictionaryTypeMask(
       TypeMask forwardTo,
@@ -22,34 +22,38 @@
       MemberEntity allocationElement,
       TypeMask keyType,
       TypeMask valueType,
-      this.typeMap)
+      this._typeMap)
       : super(forwardTo, allocationNode, allocationElement, keyType, valueType);
 
   TypeMask nullable() {
     return isNullable
         ? this
         : new DictionaryTypeMask<T>(forwardTo.nullable(), allocationNode,
-            allocationElement, keyType, valueType, typeMap);
+            allocationElement, keyType, valueType, _typeMap);
   }
 
   TypeMask nonNullable() {
     return isNullable
         ? new DictionaryTypeMask<T>(forwardTo.nonNullable(), allocationNode,
-            allocationElement, keyType, valueType, typeMap)
+            allocationElement, keyType, valueType, _typeMap)
         : this;
   }
 
   bool get isDictionary => true;
   bool get isExact => true;
 
+  bool containsKey(String key) => _typeMap.containsKey(key);
+
+  TypeMask getValueForKey(String key) => _typeMap[key];
+
   bool equalsDisregardNull(other) {
     if (other is! DictionaryTypeMask) return false;
     return allocationNode == other.allocationNode &&
         keyType == other.keyType &&
         valueType == other.valueType &&
-        typeMap.keys.every((k) => other.typeMap.containsKey(k)) &&
-        other.typeMap.keys.every(
-            (k) => typeMap.containsKey(k) && typeMap[k] == other.typeMap[k]);
+        _typeMap.keys.every((k) => other._typeMap.containsKey(k)) &&
+        other._typeMap.keys.every(
+            (k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]);
   }
 
   TypeMask intersection(TypeMask other, ClosedWorld closedWorld) {
@@ -70,14 +74,14 @@
       TypeMask newKeyType = keyType.union(other.keyType, closedWorld);
       TypeMask newValueType = valueType.union(other.valueType, closedWorld);
       Map<String, TypeMask> mappings = <String, TypeMask>{};
-      typeMap.forEach((k, v) {
-        if (!other.typeMap.containsKey(k)) {
+      _typeMap.forEach((k, dynamic v) {
+        if (!other._typeMap.containsKey(k)) {
           mappings[k] = v.nullable();
         }
       });
-      other.typeMap.forEach((k, v) {
-        if (typeMap.containsKey(k)) {
-          mappings[k] = v.union(typeMap[k], closedWorld);
+      other._typeMap.forEach((k, v) {
+        if (_typeMap.containsKey(k)) {
+          mappings[k] = v.union(_typeMap[k], closedWorld);
         } else {
           mappings[k] = v.nullable();
         }
@@ -100,11 +104,11 @@
   bool operator ==(other) => super == other;
 
   int get hashCode {
-    return computeHashCode(allocationNode, isNullable, typeMap, forwardTo);
+    return computeHashCode(allocationNode, isNullable, _typeMap, forwardTo);
   }
 
   String toString() {
     return 'Dictionary($forwardTo, key: $keyType, '
-        'value: $valueType, map: $typeMap)';
+        'value: $valueType, map: $_typeMap)';
   }
 }
diff --git a/pkg/compiler/lib/src/types/forwarding_type_mask.dart b/pkg/compiler/lib/src/types/forwarding_type_mask.dart
index 0bfed426..a24095d 100644
--- a/pkg/compiler/lib/src/types/forwarding_type_mask.dart
+++ b/pkg/compiler/lib/src/types/forwarding_type_mask.dart
@@ -122,3 +122,11 @@
 
   int get hashCode => throw "Subclass should implement hashCode getter";
 }
+
+abstract class AllocationTypeMask<T> extends ForwardingTypeMask {
+  // The [Node] where this type mask was created.
+  T get allocationNode;
+
+  // The [Entity] where this type mask was created.
+  MemberEntity get allocationElement;
+}
diff --git a/pkg/compiler/lib/src/types/map_type_mask.dart b/pkg/compiler/lib/src/types/map_type_mask.dart
index b1ded1d..7c47b4c 100644
--- a/pkg/compiler/lib/src/types/map_type_mask.dart
+++ b/pkg/compiler/lib/src/types/map_type_mask.dart
@@ -9,7 +9,7 @@
  * site of a map (currently only internal Map class) that will get specialized
  * once the [TypeGraphInferrer] phase finds a key and/or value type for it.
  */
-class MapTypeMask<T> extends ForwardingTypeMask {
+class MapTypeMask<T> extends AllocationTypeMask<T> {
   final TypeMask forwardTo;
 
   // The [Node] where this type mask was created.
diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart
index 9959309..a7da7b1 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -287,7 +287,10 @@
   bool isEmpty(TypeMask value) => value.isEmpty;
 
   @override
-  bool isExact(TypeMask value) => value.isExact || isNull(value);
+  bool isExact(TypeMask value) => value.isExact && !value.isNullable;
+
+  @override
+  bool isExactOrNull(TypeMask value) => value.isExact || isNull(value);
 
   @override
   ClassEntity getExactClass(TypeMask mask) {
@@ -298,7 +301,7 @@
   bool isPrimitiveValue(TypeMask value) => value.isValue;
 
   @override
-  ConstantValue getPrimitiveValue(TypeMask mask) {
+  PrimitiveConstantValue getPrimitiveValue(TypeMask mask) {
     if (mask.isValue) {
       ValueTypeMask valueMask = mask;
       return valueMask.value;
@@ -307,6 +310,12 @@
   }
 
   @override
+  AbstractValue createPrimitiveValue(
+      covariant TypeMask originalValue, PrimitiveConstantValue value) {
+    return new ValueTypeMask(originalValue, value);
+  }
+
+  @override
   bool canBeNull(TypeMask value) => value.isNullable;
 
   @override
@@ -489,8 +498,17 @@
   }
 
   @override
+  AbstractValue getMapKeyType(AbstractValue value) {
+    if (value is MapTypeMask) {
+      return value.keyType;
+    }
+    return dynamicType;
+  }
+
+  @override
   AbstractValue getMapValueType(AbstractValue value) {
     if (value is MapTypeMask) {
+      // TODO(johnniwinther): Assert the `value.valueType` is not null.
       return value.valueType ?? dynamicType;
     }
     return dynamicType;
@@ -505,6 +523,22 @@
   }
 
   @override
+  int getContainerLength(AbstractValue value) {
+    return value is ContainerTypeMask ? value.length : null;
+  }
+
+  @override
+  AbstractValue createContainerValue(
+      AbstractValue forwardTo,
+      Object allocationNode,
+      MemberEntity allocationElement,
+      AbstractValue elementType,
+      int length) {
+    return new ContainerTypeMask(
+        forwardTo, allocationNode, allocationElement, elementType, length);
+  }
+
+  @override
   AbstractValue unionOfMany(Iterable<AbstractValue> values) {
     TypeMask result = const TypeMask.nonNullEmpty();
     for (TypeMask value in values) {
@@ -609,4 +643,113 @@
   bool isContainer(TypeMask value) {
     return value.isContainer;
   }
+
+  @override
+  bool isDictionary(TypeMask value) {
+    return value.isDictionary;
+  }
+
+  @override
+  bool containsDictionaryKey(AbstractValue value, String key) {
+    return value is DictionaryTypeMask && value.containsKey(key);
+  }
+
+  @override
+  AbstractValue getDictionaryValueForKey(AbstractValue value, String key) {
+    if (value is DictionaryTypeMask) return value.getValueForKey(key);
+    return dynamicType;
+  }
+
+  @override
+  AbstractValue createMapValue(AbstractValue forwardTo, Object allocationNode,
+      MemberEntity allocationElement, AbstractValue key, AbstractValue value) {
+    return new MapTypeMask(
+        forwardTo, allocationNode, allocationElement, key, value);
+  }
+
+  @override
+  AbstractValue createDictionaryValue(
+      AbstractValue forwardTo,
+      Object allocationNode,
+      MemberEntity allocationElement,
+      AbstractValue key,
+      AbstractValue value,
+      Map<String, AbstractValue> mappings) {
+    return new DictionaryTypeMask(
+        forwardTo, allocationNode, allocationElement, key, value, mappings);
+  }
+
+  @override
+  bool isSpecializationOf(
+      AbstractValue specialization, AbstractValue generalization) {
+    return specialization is ForwardingTypeMask &&
+        specialization.forwardTo == generalization;
+  }
+
+  @override
+  Object getAllocationNode(AbstractValue value) {
+    if (value is AllocationTypeMask) {
+      return value.allocationNode;
+    }
+    return null;
+  }
+
+  @override
+  MemberEntity getAllocationElement(AbstractValue value) {
+    if (value is AllocationTypeMask) {
+      return value.allocationElement;
+    }
+    return null;
+  }
+
+  @override
+  AbstractValue getGeneralization(AbstractValue value) {
+    if (value is AllocationTypeMask) {
+      return value.forwardTo;
+    }
+    return null;
+  }
+
+  @override
+  String getCompactText(AbstractValue value) {
+    return formatType(value);
+  }
+}
+
+/// Convert the given TypeMask to a compact string format.
+///
+/// The default format is too verbose for the graph format since long strings
+/// create oblong nodes that obstruct the graph layout.
+String formatType(TypeMask type) {
+  if (type is FlatTypeMask) {
+    // TODO(asgerf): Disambiguate classes whose name is not unique. Using the
+    //     library name for all classes is not a good idea, since library names
+    //     can be really long and mess up the layout.
+    // Capitalize Null to emphasize that it's the null type mask and not
+    // a null value we accidentally printed out.
+    if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty';
+    String nullFlag = type.isNullable ? '?' : '';
+    String subFlag = type.isExact ? '' : type.isSubclass ? '+' : '*';
+    return '${type.base.name}$nullFlag$subFlag';
+  }
+  if (type is UnionTypeMask) {
+    return type.disjointMasks.map(formatType).join(' | ');
+  }
+  if (type is ContainerTypeMask) {
+    String container = formatType(type.forwardTo);
+    String member = formatType(type.elementType);
+    return '$container<$member>';
+  }
+  if (type is MapTypeMask) {
+    String container = formatType(type.forwardTo);
+    String key = formatType(type.keyType);
+    String value = formatType(type.valueType);
+    return '$container<$key,$value>';
+  }
+  if (type is ValueTypeMask) {
+    String baseType = formatType(type.forwardTo);
+    String value = type.value.toStructuredText();
+    return '$baseType=$value';
+  }
+  return '$type'; // Fall back on toString if not supported here.
 }