Handle ir constants in inferrer

Change-Id: Ie7b6ff9e62b3b8c78f6c04359cb72f77b249440c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97240
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index dd872ab..d5c7392 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -25,6 +25,7 @@
 import '../options.dart';
 import '../universe/selector.dart';
 import '../universe/side_effects.dart';
+import '../util/util.dart';
 import 'inferrer_engine.dart';
 import 'locals_handler.dart';
 import 'type_graph_nodes.dart';
@@ -434,11 +435,16 @@
 
   @override
   TypeInformation visitInstantiation(ir.Instantiation node) {
+    return createInstantiationTypeInformation(visit(node.expression));
+  }
+
+  TypeInformation createInstantiationTypeInformation(
+      TypeInformation expressionType) {
     // TODO(sra): Add a TypeInformation for Instantiations.  Instantiated
     // generic methods will need to be traced separately, and have the
     // information gathered in tracing reflected back to the generic method. For
     // now, pass along the uninstantiated method.
-    return visit(node.expression);
+    return expressionType;
   }
 
   @override
@@ -455,6 +461,10 @@
 
   @override
   TypeInformation visitNullLiteral(ir.NullLiteral literal) {
+    return createNullTypeInformation();
+  }
+
+  TypeInformation createNullTypeInformation() {
     return _types.nullType;
   }
 
@@ -606,14 +616,21 @@
   }
 
   @override
-  TypeInformation visitListLiteral(ir.ListLiteral listLiteral) {
+  TypeInformation visitListLiteral(ir.ListLiteral node) {
+    return createListTypeInformation(
+        node, node.expressions.map((e) => visit(e)),
+        isConst: node.isConst);
+  }
+
+  TypeInformation createListTypeInformation(
+      ir.TreeNode node, Iterable<TypeInformation> elementTypes,
+      {bool isConst}) {
     // We only set the type once. We don't need to re-visit the children
     // when re-analyzing the node.
-    return _inferrer.concreteTypes.putIfAbsent(listLiteral, () {
+    return _inferrer.concreteTypes.putIfAbsent(node, () {
       TypeInformation elementType;
       int length = 0;
-      for (ir.Expression element in listLiteral.expressions) {
-        TypeInformation type = visit(element);
+      for (TypeInformation type in elementTypes) {
         elementType = elementType == null
             ? _types.allocatePhi(null, null, type, isTry: false)
             : _types.addPhiInput(null, elementType, type);
@@ -623,18 +640,24 @@
           ? _types.nonNullEmpty()
           : _types.simplifyPhi(null, null, elementType);
       TypeInformation containerType =
-          listLiteral.isConst ? _types.constListType : _types.growableListType;
+          isConst ? _types.constListType : _types.growableListType;
       return _types.allocateList(
-          containerType, listLiteral, _analyzedMember, elementType, length);
+          containerType, node, _analyzedMember, elementType, length);
     });
   }
 
   @override
   TypeInformation visitSetLiteral(ir.SetLiteral node) {
+    return createSetTypeInformation(node, node.expressions.map((e) => visit(e)),
+        isConst: node.isConst);
+  }
+
+  TypeInformation createSetTypeInformation(
+      ir.TreeNode node, Iterable<TypeInformation> elementTypes,
+      {bool isConst}) {
     return _inferrer.concreteTypes.putIfAbsent(node, () {
       TypeInformation elementType;
-      for (ir.Expression element in node.expressions) {
-        TypeInformation type = visit(element);
+      for (TypeInformation type in elementTypes) {
         elementType = elementType == null
             ? _types.allocatePhi(null, null, type, isTry: false)
             : _types.addPhiInput(null, elementType, type);
@@ -643,7 +666,7 @@
           ? _types.nonNullEmpty()
           : _types.simplifyPhi(null, null, elementType);
       TypeInformation containerType =
-          node.isConst ? _types.constSetType : _types.setType;
+          isConst ? _types.constSetType : _types.setType;
       return _types.allocateSet(
           containerType, node, _analyzedMember, elementType);
     });
@@ -651,17 +674,24 @@
 
   @override
   TypeInformation visitMapLiteral(ir.MapLiteral node) {
+    return createMapTypeInformation(
+        node, node.entries.map((e) => new Pair(visit(e.key), visit(e.value))),
+        isConst: node.isConst);
+  }
+
+  TypeInformation createMapTypeInformation(ir.TreeNode node,
+      Iterable<Pair<TypeInformation, TypeInformation>> entryTypes,
+      {bool isConst}) {
     return _inferrer.concreteTypes.putIfAbsent(node, () {
       List keyTypes = <TypeInformation>[];
       List valueTypes = <TypeInformation>[];
 
-      for (ir.MapEntry entry in node.entries) {
-        keyTypes.add(visit(entry.key));
-        valueTypes.add(visit(entry.value));
+      for (Pair<TypeInformation, TypeInformation> entryType in entryTypes) {
+        keyTypes.add(entryType.a);
+        valueTypes.add(entryType.b);
       }
 
-      TypeInformation type =
-          node.isConst ? _types.constMapType : _types.mapType;
+      TypeInformation type = isConst ? _types.constMapType : _types.mapType;
       return _types.allocateMap(
           type, node, _analyzedMember, keyTypes, valueTypes);
     });
@@ -678,28 +708,45 @@
 
   @override
   TypeInformation visitBoolLiteral(ir.BoolLiteral node) {
-    return _types.boolLiteralType(node.value);
+    return createBoolTypeInformation(node.value);
+  }
+
+  TypeInformation createBoolTypeInformation(bool value) {
+    return _types.boolLiteralType(value);
   }
 
   @override
-  TypeInformation visitIntLiteral(ir.IntLiteral node) =>
-      // The JavaScript backend may turn this literal into a double at
-      // runtime.
-      _types.getConcreteTypeFor(_closedWorld.abstractValueDomain
-          .computeAbstractValueForConstant(
-              constant_system.createIntFromInt(node.value)));
+  TypeInformation visitIntLiteral(ir.IntLiteral node) {
+    return createIntTypeInformation(node.value);
+  }
+
+  TypeInformation createIntTypeInformation(int value) {
+    // The JavaScript backend may turn this literal into a double at
+    // runtime.
+    return _types.getConcreteTypeFor(_closedWorld.abstractValueDomain
+        .computeAbstractValueForConstant(
+            constant_system.createIntFromInt(value)));
+  }
 
   @override
-  TypeInformation visitDoubleLiteral(ir.DoubleLiteral node) =>
-      // The JavaScript backend may turn this literal into an integer at
-      // runtime.
-      _types.getConcreteTypeFor(_closedWorld.abstractValueDomain
-          .computeAbstractValueForConstant(
-              constant_system.createDouble(node.value)));
+  TypeInformation visitDoubleLiteral(ir.DoubleLiteral node) {
+    return createDoubleTypeInformation(node.value);
+  }
+
+  TypeInformation createDoubleTypeInformation(double value) {
+    // The JavaScript backend may turn this literal into a double at
+    // runtime.
+    return _types.getConcreteTypeFor(_closedWorld.abstractValueDomain
+        .computeAbstractValueForConstant(constant_system.createDouble(value)));
+  }
 
   @override
   TypeInformation visitStringLiteral(ir.StringLiteral node) {
-    return _types.stringLiteralType(node.value);
+    return createStringTypeInformation(node.value);
+  }
+
+  TypeInformation createStringTypeInformation(String value) {
+    return _types.stringLiteralType(value);
   }
 
   @override
@@ -720,12 +767,20 @@
 
   @override
   TypeInformation visitSymbolLiteral(ir.SymbolLiteral node) {
+    return createSymbolLiteralTypeInformation();
+  }
+
+  TypeInformation createSymbolLiteralTypeInformation() {
     return _types
         .nonNullSubtype(_closedWorld.commonElements.symbolImplementationClass);
   }
 
   @override
   TypeInformation visitTypeLiteral(ir.TypeLiteral node) {
+    return createTypeLiteralInformation();
+  }
+
+  TypeInformation createTypeLiteralInformation() {
     return _types.typeType;
   }
 
@@ -1065,7 +1120,11 @@
   /// Try to find the length given to a fixed array constructor call.
   int _findLength(ir.Arguments arguments) {
     ir.Expression firstArgument = arguments.positional.first;
-    if (firstArgument is ir.IntLiteral) {
+    if (firstArgument is ir.ConstantExpression &&
+        firstArgument.constant is ir.IntConstant) {
+      ir.IntConstant constant = firstArgument.constant;
+      return constant.value;
+    } else if (firstArgument is ir.IntLiteral) {
       return firstArgument.value;
     } else if (firstArgument is ir.StaticGet) {
       MemberEntity member = _elementMap.getMember(firstArgument.target);
@@ -1231,8 +1290,14 @@
 
   @override
   TypeInformation visitStaticGet(ir.StaticGet node) {
-    MemberEntity member = _elementMap.getMember(node.target);
     AbstractValue mask = _memberData.typeOfSend(node);
+    assert(mask == null);
+    return createStaticGetTypeInformation(node, node.target, mask: mask);
+  }
+
+  TypeInformation createStaticGetTypeInformation(ir.Node node, ir.Member target,
+      {AbstractValue mask}) {
+    MemberEntity member = _elementMap.getMember(target);
     return handleStaticInvoke(
         node, new Selector.getter(member.memberName), mask, member, null);
   }
@@ -1765,8 +1830,111 @@
 
   @override
   TypeInformation visitConstantExpression(ir.ConstantExpression node) {
-    // TODO(johnniwinther,fishythefish): Compute the precise type information.
-    return _types.dynamicType;
+    return node.constant.accept(new TypeInformationConstantVisitor(this, node));
+  }
+}
+
+class TypeInformationConstantVisitor
+    implements ir.ConstantVisitor<TypeInformation> {
+  final KernelTypeGraphBuilder builder;
+  final ir.ConstantExpression expression;
+
+  TypeInformationConstantVisitor(this.builder, this.expression);
+
+  @override
+  TypeInformation defaultConstant(ir.Constant node) {
+    throw new UnsupportedError("Unexpected constant: "
+        "${node} (${node.runtimeType})");
+  }
+
+  @override
+  TypeInformation visitNullConstant(ir.NullConstant node) {
+    return builder.createNullTypeInformation();
+  }
+
+  @override
+  TypeInformation visitBoolConstant(ir.BoolConstant node) {
+    return builder.createBoolTypeInformation(node.value);
+  }
+
+  @override
+  TypeInformation visitIntConstant(ir.IntConstant node) {
+    return builder.createIntTypeInformation(node.value);
+  }
+
+  @override
+  TypeInformation visitDoubleConstant(ir.DoubleConstant node) {
+    return builder.createDoubleTypeInformation(node.value);
+  }
+
+  @override
+  TypeInformation visitStringConstant(ir.StringConstant node) {
+    return builder.createStringTypeInformation(node.value);
+  }
+
+  @override
+  TypeInformation visitSymbolConstant(ir.SymbolConstant node) {
+    return builder.createSymbolLiteralTypeInformation();
+  }
+
+  @override
+  TypeInformation visitMapConstant(ir.MapConstant node) {
+    return builder.createMapTypeInformation(
+        new ConstantReference(expression, node),
+        node.entries
+            .map((e) => new Pair(e.key.accept(this), e.value.accept(this))),
+        isConst: true);
+  }
+
+  @override
+  TypeInformation visitListConstant(ir.ListConstant node) {
+    return builder.createListTypeInformation(
+        new ConstantReference(expression, node),
+        node.entries.map((e) => e.accept(this)),
+        isConst: true);
+  }
+
+  @override
+  TypeInformation visitSetConstant(ir.SetConstant node) {
+    return builder.createSetTypeInformation(
+        new ConstantReference(expression, node),
+        node.entries.map((e) => e.accept(this)),
+        isConst: true);
+  }
+
+  @override
+  TypeInformation visitInstanceConstant(ir.InstanceConstant node) {
+    node.fieldValues.forEach((ir.Reference reference, ir.Constant value) {
+      builder._inferrer.recordTypeOfField(
+          builder._elementMap.getField(reference.asField), value.accept(this));
+    });
+    return builder._types.getConcreteTypeFor(builder
+        ._closedWorld.abstractValueDomain
+        .createNonNullExact(builder._elementMap.getClass(node.classNode)));
+  }
+
+  @override
+  TypeInformation visitPartialInstantiationConstant(
+      ir.PartialInstantiationConstant node) {
+    return builder
+        .createInstantiationTypeInformation(node.tearOffConstant.accept(this));
+  }
+
+  @override
+  TypeInformation visitTearOffConstant(ir.TearOffConstant node) {
+    return builder.createStaticGetTypeInformation(node, node.procedure);
+  }
+
+  @override
+  TypeInformation visitTypeLiteralConstant(ir.TypeLiteralConstant node) {
+    return builder.createTypeLiteralInformation();
+  }
+
+  @override
+  TypeInformation visitUnevaluatedConstant(ir.UnevaluatedConstant node) {
+    // TODO(johnniwinther): These should have been replaced by their evaluated
+    // counterpart during impact building.
+    return builder._types.dynamicType;
   }
 }
 
@@ -1991,3 +2159,51 @@
     return sb.toString();
   }
 }
+
+/// Class to represent a reference to a constant in allocation nodes.
+///
+/// This class is needed in order to support serialization of references to
+/// constant nodes. Since the constant nodes are not [ir.TreeNode]s we can only
+/// serialize the constants as values which would bypass by the canonicalization
+/// performed by the CFE. This class extends only as a trick to easily pass
+/// it through serialization.
+///
+/// By adding a reference to the constant expression in which the constant
+/// occurred, we can serialize references to constants in two steps: a reference
+/// to the constant expression followed by an index of the referred constant
+/// in the traversal order of the constant held by the constant expression.
+///
+/// This is used for list, map, and set literals.
+class ConstantReference extends ir.TreeNode {
+  final ir.ConstantExpression expression;
+  final ir.Constant constant;
+
+  ConstantReference(this.expression, this.constant);
+
+  @override
+  void visitChildren(ir.Visitor v) {
+    throw new UnsupportedError("ConstantReference.visitChildren");
+  }
+
+  @override
+  accept(ir.TreeVisitor v) {
+    throw new UnsupportedError("ConstantReference.accept");
+  }
+
+  @override
+  transformChildren(ir.Transformer v) {
+    throw new UnsupportedError("ConstantReference.transformChildren");
+  }
+
+  @override
+  int get hashCode => 13 * constant.hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    return other is ConstantReference && constant == other.constant;
+  }
+
+  @override
+  String toString() => 'ConstantReference(constant=$constant)';
+}
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 5e12d5a..a33cffc 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -72,7 +72,7 @@
   NoSuchMethodData get noSuchMethodData => closedWorld.noSuchMethodData;
 
   TypeSystem get types;
-  Map<ir.Node, TypeInformation> get concreteTypes;
+  Map<ir.TreeNode, TypeInformation> get concreteTypes;
   InferredDataBuilder get inferredDataBuilder;
 
   FunctionEntity get mainElement;
@@ -281,8 +281,8 @@
   @override
   final TypeSystem types;
   @override
-  final Map<ir.Node, TypeInformation> concreteTypes =
-      new Map<ir.Node, TypeInformation>();
+  final Map<ir.TreeNode, TypeInformation> concreteTypes =
+      new Map<ir.TreeNode, TypeInformation>();
 
   final Set<ConstructorEntity> generativeConstructorsExposingThis =
       new Set<ConstructorEntity>();
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index aa827d3..c5c32cf 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -431,7 +431,7 @@
   }
 
   TypeInformation allocateList(
-      TypeInformation type, ir.Node node, MemberEntity enclosing,
+      TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
       [TypeInformation elementType, int length]) {
     assert(strategy.checkListNode(node));
     ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
@@ -472,7 +472,7 @@
   }
 
   TypeInformation allocateSet(
-      TypeInformation type, ir.Node node, MemberEntity enclosing,
+      TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
       [TypeInformation elementType]) {
     assert(strategy.checkSetNode(node));
     bool isConst = type.type == _abstractValueDomain.constSetType;
@@ -491,7 +491,7 @@
   }
 
   TypeInformation allocateMap(
-      ConcreteTypeInformation type, ir.Node node, MemberEntity element,
+      ConcreteTypeInformation type, ir.TreeNode node, MemberEntity element,
       [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) {
     assert(strategy.checkMapNode(node));
     assert(keyTypes.length == valueTypes.length);
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart
index 17e4913..f2fda3d 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart
@@ -16,7 +16,7 @@
   final TypeMask forwardTo;
 
   @override
-  final ir.TreeNode allocationNode;
+  final ir.Node allocationNode;
 
   @override
   final MemberEntity allocationElement;
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart
index 827840c..ef15561 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart
@@ -20,7 +20,7 @@
 
   DictionaryTypeMask(
       TypeMask forwardTo,
-      ir.TreeNode allocationNode,
+      ir.Node allocationNode,
       MemberEntity allocationElement,
       TypeMask keyType,
       TypeMask valueType,
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
index 39f850c..8d86f0c 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
@@ -154,8 +154,8 @@
 }
 
 abstract class AllocationTypeMask extends ForwardingTypeMask {
-  // The [ir.TreeNode] where this type mask was created.
-  ir.TreeNode get allocationNode;
+  // The [ir.Node] where this type mask was created.
+  ir.Node get allocationNode;
 
   // The [Entity] where this type mask was created.
   MemberEntity get allocationElement;
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart
index b1d32eb..3b4d729 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart
@@ -16,7 +16,7 @@
   final TypeMask forwardTo;
 
   @override
-  final ir.TreeNode allocationNode;
+  final ir.Node allocationNode;
 
   @override
   final MemberEntity allocationElement;
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart
index f95bac6..0b1ffb6e 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart
@@ -16,7 +16,7 @@
   final TypeMask forwardTo;
 
   @override
-  final ir.TreeNode allocationNode;
+  final ir.Node allocationNode;
 
   @override
   final MemberEntity allocationElement;
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index 737c314..dfe4e9b 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -232,6 +232,12 @@
     } else if (value is ir.TypeParameter) {
       _writeEnumInternal(_TreeNodeKind.typeParameter);
       _writeTypeParameter(value);
+    } else if (value is ConstantReference) {
+      _writeEnumInternal(_TreeNodeKind.constant);
+      _writeTreeNode(value.expression);
+      _ConstantNodeIndexerVisitor indexer = new _ConstantNodeIndexerVisitor();
+      value.expression.constant.accept(indexer);
+      _writeIntInternal(indexer.getIndex(value.constant));
     } else {
       _writeEnumInternal(_TreeNodeKind.node);
       ir.TreeNode member = value;
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index b9660bd..f4160ce 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -518,6 +518,14 @@
         return _readFunctionNode();
       case _TreeNodeKind.typeParameter:
         return _readTypeParameter();
+      case _TreeNodeKind.constant:
+        // TODO(johnniwinther): Support serialization within a member context
+        // and use this to temporarily cache constant node indices.
+        ir.ConstantExpression expression = _readTreeNode();
+        _ConstantNodeIndexerVisitor indexer = new _ConstantNodeIndexerVisitor();
+        expression.constant.accept(indexer);
+        ir.Constant constant = indexer.getConstant(_readIntInternal());
+        return new ConstantReference(expression, constant);
       case _TreeNodeKind.node:
         _MemberData data = _readMemberData();
         int index = _readIntInternal();
diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart
index 4bb57a4..40cc83d 100644
--- a/pkg/compiler/lib/src/serialization/helpers.dart
+++ b/pkg/compiler/lib/src/serialization/helpers.dart
@@ -48,7 +48,8 @@
   node,
   functionNode,
   typeParameter,
-  functionDeclarationVariable
+  functionDeclarationVariable,
+  constant,
 }
 
 /// Enum used for identifying [ir.FunctionNode] context in serialization.
diff --git a/pkg/compiler/lib/src/serialization/node_indexer.dart b/pkg/compiler/lib/src/serialization/node_indexer.dart
index f54ac9c..8e92ebf 100644
--- a/pkg/compiler/lib/src/serialization/node_indexer.dart
+++ b/pkg/compiler/lib/src/serialization/node_indexer.dart
@@ -193,4 +193,108 @@
     registerNode(node);
     super.visitSuperPropertyGet(node);
   }
+
+  @override
+  void visitConstantExpression(ir.ConstantExpression node) {
+    registerNode(node);
+    super.visitConstantExpression(node);
+  }
+}
+
+/// Visitor that ascribes an index to all [ir.Constant]s that we potentially
+/// need to reference for serialization and deserialization.
+///
+/// Currently this is only list, map, and set constants, which are used as
+/// allocation identities in the global inference.
+class _ConstantNodeIndexerVisitor implements ir.ConstantVisitor<void> {
+  int _currentIndex = 0;
+  final Map<int, ir.Constant> _indexToNodeMap = {};
+  final Map<ir.Constant, int> _nodeToIndexMap = {};
+
+  void registerConstant(ir.Constant node) {
+    _indexToNodeMap[_currentIndex] = node;
+    _nodeToIndexMap[node] = _currentIndex;
+    _currentIndex++;
+  }
+
+  int getIndex(ir.Constant node) {
+    assert(_nodeToIndexMap.containsKey(node), "Constant without index: $node");
+    return _nodeToIndexMap[node];
+  }
+
+  ir.Constant getConstant(int index) {
+    assert(
+        _indexToNodeMap.containsKey(index), "Index without constant: $index");
+    return _indexToNodeMap[index];
+  }
+
+  @override
+  void visitUnevaluatedConstant(ir.UnevaluatedConstant node) {}
+
+  @override
+  void visitTypeLiteralConstant(ir.TypeLiteralConstant node) {}
+
+  @override
+  void visitTearOffConstant(ir.TearOffConstant node) {}
+
+  @override
+  void visitPartialInstantiationConstant(ir.PartialInstantiationConstant node) {
+    node.tearOffConstant.accept(this);
+  }
+
+  @override
+  void visitInstanceConstant(ir.InstanceConstant node) {
+    node.fieldValues.forEach((_, ir.Constant value) {
+      value.accept(this);
+    });
+  }
+
+  @override
+  void visitSetConstant(ir.SetConstant node) {
+    registerConstant(node);
+    for (ir.Constant element in node.entries) {
+      element.accept(this);
+    }
+  }
+
+  @override
+  void visitListConstant(ir.ListConstant node) {
+    registerConstant(node);
+    for (ir.Constant element in node.entries) {
+      element.accept(this);
+    }
+  }
+
+  @override
+  void visitMapConstant(ir.MapConstant node) {
+    registerConstant(node);
+    for (ir.ConstantMapEntry entry in node.entries) {
+      entry.key.accept(this);
+      entry.value.accept(this);
+    }
+  }
+
+  @override
+  void visitSymbolConstant(ir.SymbolConstant node) {}
+
+  @override
+  void visitStringConstant(ir.StringConstant node) {}
+
+  @override
+  void visitDoubleConstant(ir.DoubleConstant node) {}
+
+  @override
+  void visitIntConstant(ir.IntConstant node) {}
+
+  @override
+  void visitBoolConstant(ir.BoolConstant node) {}
+
+  @override
+  void visitNullConstant(ir.NullConstant node) {}
+
+  @override
+  void defaultConstant(ir.Constant node) {
+    throw new UnimplementedError(
+        "Unexpected constant: $node (${node.runtimeType})");
+  }
 }
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index b974951..65276b3 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -15,6 +15,7 @@
 import '../elements/entities.dart';
 import '../elements/indexed.dart';
 import '../elements/types.dart';
+import '../inferrer/builder_kernel.dart';
 import '../ir/static_type_base.dart';
 import '../js_model/closure.dart';
 import '../js_model/locals.dart';
diff --git a/tests/compiler/dart2js/inference/data/call_method_function_typed_value.dart b/tests/compiler/dart2js/inference/data/call_method_function_typed_value.dart
index a70e567..9fc9e74 100644
--- a/tests/compiler/dart2js/inference/data/call_method_function_typed_value.dart
+++ b/tests/compiler/dart2js/inference/data/call_method_function_typed_value.dart
@@ -11,6 +11,8 @@
         int
             /*strong.[null|subclass=Object]*/
             /*omit.[null|subclass=JSInt]*/
+            /*strongConst.[null|subclass=Object]*/
+            /*omitConst.[null|subclass=JSInt]*/
             i) =>
     2 /*invoke: [exact=JSUInt31]*/ * i;
 
diff --git a/tests/compiler/dart2js/inference/data/closure_tracer_28919.dart b/tests/compiler/dart2js/inference/data/closure_tracer_28919.dart
index 7f88ade..6bc222f 100644
--- a/tests/compiler/dart2js/inference/data/closure_tracer_28919.dart
+++ b/tests/compiler/dart2js/inference/data/closure_tracer_28919.dart
@@ -57,6 +57,8 @@
         /*[null]*/ (int
             /*strong.[null|subclass=Object]*/
             /*omit.[null|subclass=JSInt]*/
+            /*strongConst.[null|subclass=Object]*/
+            /*omitConst.[null|subclass=JSInt]*/
             x) {
       res = x;
       sum = x /*invoke: [null|subclass=JSInt]*/ + i;
diff --git a/tests/compiler/dart2js/inference/data/const_closure.dart b/tests/compiler/dart2js/inference/data/const_closure.dart
index 137c2b7..8425f4d 100644
--- a/tests/compiler/dart2js/inference/data/const_closure.dart
+++ b/tests/compiler/dart2js/inference/data/const_closure.dart
@@ -13,10 +13,12 @@
   return a;
 }
 
-/*element: foo1:[subclass=Closure]*/
+/*strong.element: foo1:[subclass=Closure]*/
+/*omit.element: foo1:[subclass=Closure]*/
 const foo1 = method1;
 
-/*element: foo2:[subclass=Closure]*/
+/*strong.element: foo2:[subclass=Closure]*/
+/*omit.element: foo2:[subclass=Closure]*/
 const foo2 = method2;
 
 /*element: returnInt1:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/const_closure2.dart b/tests/compiler/dart2js/inference/data/const_closure2.dart
index 26b76ff..207a431 100644
--- a/tests/compiler/dart2js/inference/data/const_closure2.dart
+++ b/tests/compiler/dart2js/inference/data/const_closure2.dart
@@ -8,7 +8,8 @@
   return a;
 }
 
-/*element: foo:[subclass=Closure]*/
+/*strong.element: foo:[subclass=Closure]*/
+/*omit.element: foo:[subclass=Closure]*/
 const foo = method;
 
 /*element: returnNum:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/const_closure3.dart b/tests/compiler/dart2js/inference/data/const_closure3.dart
index 74b97a0..79a36f0 100644
--- a/tests/compiler/dart2js/inference/data/const_closure3.dart
+++ b/tests/compiler/dart2js/inference/data/const_closure3.dart
@@ -8,7 +8,8 @@
   return a;
 }
 
-/*element: foo:[subclass=Closure]*/
+/*strong.element: foo:[subclass=Closure]*/
+/*omit.element: foo:[subclass=Closure]*/
 const foo = method;
 
 /*element: returnInt:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/const_closure4.dart b/tests/compiler/dart2js/inference/data/const_closure4.dart
index 26b76ff..207a431 100644
--- a/tests/compiler/dart2js/inference/data/const_closure4.dart
+++ b/tests/compiler/dart2js/inference/data/const_closure4.dart
@@ -8,7 +8,8 @@
   return a;
 }
 
-/*element: foo:[subclass=Closure]*/
+/*strong.element: foo:[subclass=Closure]*/
+/*omit.element: foo:[subclass=Closure]*/
 const foo = method;
 
 /*element: returnNum:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/const_closure5.dart b/tests/compiler/dart2js/inference/data/const_closure5.dart
index 3055295..89a3abf 100644
--- a/tests/compiler/dart2js/inference/data/const_closure5.dart
+++ b/tests/compiler/dart2js/inference/data/const_closure5.dart
@@ -8,7 +8,8 @@
   return a;
 }
 
-/*element: foo:[subclass=Closure]*/
+/*strong.element: foo:[subclass=Closure]*/
+/*omit.element: foo:[subclass=Closure]*/
 const foo = method;
 
 /*element: returnInt:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/enum.dart b/tests/compiler/dart2js/inference/data/enum.dart
index 1fc823b..79b6d84 100644
--- a/tests/compiler/dart2js/inference/data/enum.dart
+++ b/tests/compiler/dart2js/inference/data/enum.dart
@@ -16,7 +16,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum Enum1 {
-  /*element: Enum1.a:[exact=Enum1]*/
+  /*strong.element: Enum1.a:[exact=Enum1]*/
+  /*omit.element: Enum1.a:[exact=Enum1]*/
   a,
 }
 
@@ -28,7 +29,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum Enum2 {
-  /*element: Enum2.a:[exact=Enum2]*/
+  /*strong.element: Enum2.a:[exact=Enum2]*/
+  /*omit.element: Enum2.a:[exact=Enum2]*/
   a,
 }
 
@@ -40,9 +42,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum Enum3 {
-  /*element: Enum3.a:[exact=Enum3]*/
+  /*strong.element: Enum3.a:[exact=Enum3]*/
+  /*omit.element: Enum3.a:[exact=Enum3]*/
   a,
-  /*element: Enum3.b:[exact=Enum3]*/
+  /*strong.element: Enum3.b:[exact=Enum3]*/
+  /*omit.element: Enum3.b:[exact=Enum3]*/
   b,
 }
 
@@ -54,7 +58,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum Enum4 {
-  /*element: Enum4.a:[exact=Enum4]*/
+  /*strong.element: Enum4.a:[exact=Enum4]*/
+  /*omit.element: Enum4.a:[exact=Enum4]*/
   a,
 }
 
@@ -68,9 +73,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum Enum5 {
-  /*element: Enum5.a:[exact=Enum5]*/
+  /*strong.element: Enum5.a:[exact=Enum5]*/
+  /*omit.element: Enum5.a:[exact=Enum5]*/
   a,
-  /*element: Enum5.b:[exact=Enum5]*/
+  /*strong.element: Enum5.b:[exact=Enum5]*/
+  /*omit.element: Enum5.b:[exact=Enum5]*/
   b,
 }
 
diff --git a/tests/compiler/dart2js/inference/data/global_field_closure.dart b/tests/compiler/dart2js/inference/data/global_field_closure.dart
index d2fd3bc..d40bfaab 100644
--- a/tests/compiler/dart2js/inference/data/global_field_closure.dart
+++ b/tests/compiler/dart2js/inference/data/global_field_closure.dart
@@ -13,10 +13,16 @@
   return a;
 }
 
-/*element: foo1:[null|subclass=Closure]*/
+/*strong.element: foo1:[null|subclass=Closure]*/
+/*omit.element: foo1:[null|subclass=Closure]*/
+/*strongConst.element: foo1:[subclass=Closure]*/
+/*omitConst.element: foo1:[subclass=Closure]*/
 var foo1 = method1;
 
-/*element: foo2:[null|subclass=Closure]*/
+/*strong.element: foo2:[null|subclass=Closure]*/
+/*omit.element: foo2:[null|subclass=Closure]*/
+/*strongConst.element: foo2:[subclass=Closure]*/
+/*omitConst.element: foo2:[subclass=Closure]*/
 var foo2 = method2;
 
 /*element: returnInt1:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/global_field_closure2.dart b/tests/compiler/dart2js/inference/data/global_field_closure2.dart
index 765913e..f4199e9 100644
--- a/tests/compiler/dart2js/inference/data/global_field_closure2.dart
+++ b/tests/compiler/dart2js/inference/data/global_field_closure2.dart
@@ -8,7 +8,10 @@
   return a;
 }
 
-/*element: foo:[null|subclass=Closure]*/
+/*strong.element: foo:[null|subclass=Closure]*/
+/*omit.element: foo:[null|subclass=Closure]*/
+/*strongConst.element: foo:[subclass=Closure]*/
+/*omitConst.element: foo:[subclass=Closure]*/
 var foo = method;
 
 /*element: returnInt:[null|subclass=Object]*/
diff --git a/tests/compiler/dart2js/inference/data/list.dart b/tests/compiler/dart2js/inference/data/list.dart
index 85919b2..967ed10 100644
--- a/tests/compiler/dart2js/inference/data/list.dart
+++ b/tests/compiler/dart2js/inference/data/list.dart
@@ -108,7 +108,8 @@
 // Create a Uint16List using a const top-level field as length.
 ////////////////////////////////////////////////////////////////////////////////
 
-/*element: _field3:[exact=JSUInt31]*/
+/*strong.element: _field3:[exact=JSUInt31]*/
+/*omit.element: _field3:[exact=JSUInt31]*/
 const _field3 = 12;
 
 /*element: newUint16List:Container([exact=NativeUint16List], element: [exact=JSUInt31], length: 12)*/
@@ -134,7 +135,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 abstract class Class1 {
-  /*element: Class1.field:[exact=JSUInt31]*/
+  /*strong.element: Class1.field:[exact=JSUInt31]*/
+  /*omit.element: Class1.field:[exact=JSUInt31]*/
   static const field = 15;
 }
 
diff --git a/tests/compiler/dart2js/inference/data/map_tracer_const.dart b/tests/compiler/dart2js/inference/data/map_tracer_const.dart
index ab50dff..05cd163 100644
--- a/tests/compiler/dart2js/inference/data/map_tracer_const.dart
+++ b/tests/compiler/dart2js/inference/data/map_tracer_const.dart
@@ -7,15 +7,21 @@
     int
         /*strong.Union([exact=JSDouble], [exact=JSUInt31])*/
         /*omit.[exact=JSUInt31]*/
+        /*strongConst.Union([exact=JSDouble], [exact=JSUInt31])*/
+        /*omitConst.[exact=JSUInt31]*/
         x) {
   return x;
 }
 
 class A {
-  /*element: A.DEFAULT:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*strong.element: A.DEFAULT:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*omit.element: A.DEFAULT:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
   static const DEFAULT = const {'fun': closure};
 
-  /*element: A.map:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*strong.element: A.map:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*omit.element: A.map:Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*strongConst.element: A.map:Dictionary([exact=ConstantStringMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+  /*omitConst.element: A.map:Dictionary([exact=ConstantStringMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
   final map;
 
   /*element: A.:[exact=A]*/
@@ -26,7 +32,10 @@
 main() {
   var a = new A();
   a. /*[exact=A]*/ map
-      /*Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+      /*strong.Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+      /*omit.Dictionary([subclass=ConstantMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+      /*strongConst.Dictionary([exact=ConstantStringMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
+      /*omitConst.Dictionary([exact=ConstantStringMap], key: Value([exact=JSString], value: "fun"), value: [null|subclass=Closure], map: {fun: [subclass=Closure]})*/
       ['fun'](3.3);
   print(closure(22));
 }
diff --git a/tests/compiler/dart2js/inference/data/native.dart b/tests/compiler/dart2js/inference/data/native.dart
index e9ec7b9..a6cb859 100644
--- a/tests/compiler/dart2js/inference/data/native.dart
+++ b/tests/compiler/dart2js/inference/data/native.dart
@@ -7,7 +7,11 @@
   nativeMethod();
 }
 
-/*element: nativeMethod:[null|subclass=Object]*/
+/*strong.element: nativeMethod:[null|subclass=Object]*/
+/*omit.element: nativeMethod:[null|subclass=Object]*/
+// TODO(johnniwinther): Support native behavior from CFE constants:
+/*strongConst.element: nativeMethod:[null]*/
+/*omitConst.element: nativeMethod:[null]*/
 nativeMethod()
     // ignore: NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE
     native;
diff --git a/tests/compiler/dart2js/inference/data/native2.dart b/tests/compiler/dart2js/inference/data/native2.dart
index bae53e9..86b88a5 100644
--- a/tests/compiler/dart2js/inference/data/native2.dart
+++ b/tests/compiler/dart2js/inference/data/native2.dart
@@ -12,7 +12,11 @@
   createRectangle();
 }
 
-/*element: createElement:[null|subclass=Element]*/
+/*strong.element: createElement:[null|subclass=Element]*/
+/*omit.element: createElement:[null|subclass=Element]*/
+// TODO(johnniwinther): Support native behavior from CFE constants:
+/*strongConst.element: createElement:[null]*/
+/*omitConst.element: createElement:[null]*/
 Element createElement()
     // ignore: NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE
     native;
diff --git a/tests/compiler/dart2js/inference/data/native3.dart b/tests/compiler/dart2js/inference/data/native3.dart
index 73e458f..1c885db 100644
--- a/tests/compiler/dart2js/inference/data/native3.dart
+++ b/tests/compiler/dart2js/inference/data/native3.dart
@@ -9,7 +9,11 @@
   createRectangle();
 }
 
-/*element: createRectangle:[null|subclass=DomRectReadOnly]*/
+/*strong.element: createRectangle:[null|subclass=DomRectReadOnly]*/
+/*omit.element: createRectangle:[null|subclass=DomRectReadOnly]*/
+// TODO(johnniwinther): Support native behavior from CFE constants:
+/*strongConst.element: createRectangle:[null]*/
+/*omitConst.element: createRectangle:[null]*/
 Rectangle createRectangle()
     // ignore: NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE
     native;
diff --git a/tests/compiler/dart2js/inference/data/no_such_method.dart b/tests/compiler/dart2js/inference/data/no_such_method.dart
index 16f5358..2803433 100644
--- a/tests/compiler/dart2js/inference/data/no_such_method.dart
+++ b/tests/compiler/dart2js/inference/data/no_such_method.dart
@@ -17,6 +17,8 @@
           Invocation
               /*strong.[null|subclass=Object]*/
               /*omit.[null|exact=JSInvocationMirror]*/
+              /*strongConst.[null|subclass=Object]*/
+              /*omitConst.[null|exact=JSInvocationMirror]*/
               _) =>
       42;
 
@@ -41,6 +43,8 @@
           Invocation
               /*strong.[null|subclass=Object]*/
               /*omit.[null|exact=JSInvocationMirror]*/
+              /*strongConst.[null|subclass=Object]*/
+              /*omitConst.[null|exact=JSInvocationMirror]*/
               _) =>
       42;
 
@@ -65,6 +69,8 @@
       Invocation
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           invocation) {
     return invocation
         .
@@ -101,6 +107,8 @@
       Invocation
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           invocation) {
     this. /*update: [exact=Class4]*/ field = invocation
         .
diff --git a/tests/compiler/dart2js/inference/data/no_such_method1.dart b/tests/compiler/dart2js/inference/data/no_such_method1.dart
index 2e9aadb..eeaed0e 100644
--- a/tests/compiler/dart2js/inference/data/no_such_method1.dart
+++ b/tests/compiler/dart2js/inference/data/no_such_method1.dart
@@ -8,6 +8,8 @@
   noSuchMethod(
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           im) =>
       42;
 }
diff --git a/tests/compiler/dart2js/inference/data/no_such_method2.dart b/tests/compiler/dart2js/inference/data/no_such_method2.dart
index 2f0741b..3bce145 100644
--- a/tests/compiler/dart2js/inference/data/no_such_method2.dart
+++ b/tests/compiler/dart2js/inference/data/no_such_method2.dart
@@ -8,6 +8,8 @@
   noSuchMethod(
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           im) =>
       42;
 }
@@ -33,6 +35,8 @@
   noSuchMethod(
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           im) =>
       42.5;
 }
diff --git a/tests/compiler/dart2js/inference/data/no_such_method3.dart b/tests/compiler/dart2js/inference/data/no_such_method3.dart
index c89382c..07c56bd 100644
--- a/tests/compiler/dart2js/inference/data/no_such_method3.dart
+++ b/tests/compiler/dart2js/inference/data/no_such_method3.dart
@@ -10,6 +10,8 @@
   noSuchMethod(
           /*strong.[null|subclass=Object]*/
           /*omit.[null|exact=JSInvocationMirror]*/
+          /*strongConst.[null|subclass=Object]*/
+          /*omitConst.[null|exact=JSInvocationMirror]*/
           im) =>
       throw 'foo';
 }
diff --git a/tests/compiler/dart2js/inference/data/parameters_trust.dart b/tests/compiler/dart2js/inference/data/parameters_trust.dart
index fae5e390..27b45a3 100644
--- a/tests/compiler/dart2js/inference/data/parameters_trust.dart
+++ b/tests/compiler/dart2js/inference/data/parameters_trust.dart
@@ -18,6 +18,8 @@
     int
         /*strong.Union([exact=JSString], [exact=JSUInt31])*/
         /*omit.[exact=JSUInt31]*/
+        /*strongConst.Union([exact=JSString], [exact=JSUInt31])*/
+        /*omitConst.[exact=JSUInt31]*/
         i) {
   return i;
 }
diff --git a/tests/compiler/dart2js/inference/data/static.dart b/tests/compiler/dart2js/inference/data/static.dart
index 04fa7c3..4b65a4c 100644
--- a/tests/compiler/dart2js/inference/data/static.dart
+++ b/tests/compiler/dart2js/inference/data/static.dart
@@ -189,7 +189,10 @@
 /*element: _method1:[exact=JSUInt31]*/
 _method1() => 42;
 
-/*element: _field2:[null|subclass=Closure]*/
+/*strong.element: _field2:[null|subclass=Closure]*/
+/*omit.element: _field2:[null|subclass=Closure]*/
+/*strongConst.element: _field2:[subclass=Closure]*/
+/*omitConst.element: _field2:[subclass=Closure]*/
 dynamic _field2 = _method1;
 
 /*element: invokeStaticFieldTearOff:[null|subclass=Object]*/
@@ -202,7 +205,10 @@
 /*element: _method5:Value([exact=JSString], value: "")*/
 String _method5() => '';
 
-/*element: _field5:[null|subclass=Closure]*/
+/*strong.element: _field5:[null|subclass=Closure]*/
+/*omit.element: _field5:[null|subclass=Closure]*/
+/*strongConst.element: _field5:[subclass=Closure]*/
+/*omitConst.element: _field5:[subclass=Closure]*/
 String Function() _field5 = _method5;
 
 /*element: invokeStaticTypedFieldTearOff:[null|exact=JSString]*/
@@ -216,7 +222,10 @@
 /*element: _method2:[exact=JSUInt31]*/
 _method2(/*[exact=JSUInt31]*/ o) => 42;
 
-/*element: _field3:[null|subclass=Closure]*/
+/*strong.element: _field3:[null|subclass=Closure]*/
+/*omit.element: _field3:[null|subclass=Closure]*/
+/*strongConst.element: _field3:[subclass=Closure]*/
+/*omitConst.element: _field3:[subclass=Closure]*/
 dynamic _field3 = _method2;
 
 /*element: invokeStaticFieldTearOffParameters:[null|subclass=Object]*/
@@ -242,10 +251,16 @@
 /*element: _method6:[exact=JSUInt31]*/
 int _method6() => 0;
 
-/*element: _field7:[null|subclass=Closure]*/
+/*strong.element: _field7:[null|subclass=Closure]*/
+/*omit.element: _field7:[null|subclass=Closure]*/
+/*strongConst.element: _field7:[subclass=Closure]*/
+/*omitConst.element: _field7:[subclass=Closure]*/
 int Function() _field7 = _method6;
 
-/*element: _getter3:[null|subclass=Closure]*/
+/*strong.element: _getter3:[null|subclass=Closure]*/
+/*omit.element: _getter3:[null|subclass=Closure]*/
+/*strongConst.element: _getter3:[subclass=Closure]*/
+/*omitConst.element: _getter3:[subclass=Closure]*/
 int Function() get _getter3 => _field7;
 
 /*element: invokeStaticTypedGetterTearOff:[null|subclass=JSInt]*/
@@ -284,7 +299,10 @@
 /// arguments.
 ////////////////////////////////////////////////////////////////////////////////
 
-/*element: _field4:[null|subclass=Closure]*/
+/*strong.element: _field4:[null|subclass=Closure]*/
+/*omit.element: _field4:[null|subclass=Closure]*/
+/*strongConst.element: _field4:[subclass=Closure]*/
+/*omitConst.element: _field4:[subclass=Closure]*/
 T Function<T>(T) _field4 = _method4;
 
 /*element: invokeStaticGenericField1:[null|subclass=JSInt]*/
diff --git a/tests/compiler/dart2js/inference/inference_test_helper.dart b/tests/compiler/dart2js/inference/inference_test_helper.dart
index 5b5eb7f..9259adb 100644
--- a/tests/compiler/dart2js/inference/inference_test_helper.dart
+++ b/tests/compiler/dart2js/inference/inference_test_helper.dart
@@ -35,6 +35,7 @@
         forUserLibrariesOnly: true,
         args: args,
         options: [stopAfterTypeInference],
+        testCFEConstants: true,
         skipForStrong: skipForStrong,
         shardIndex: shardIndex ?? 0,
         shards: shardIndex != null ? 2 : 1);
diff --git a/tests/compiler/dart2js/serialization/data/const_literals.dart b/tests/compiler/dart2js/serialization/data/const_literals.dart
new file mode 100644
index 0000000..fb8128a
--- /dev/null
+++ b/tests/compiler/dart2js/serialization/data/const_literals.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, 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.
+
+main() {
+  mapConstLiteral();
+  dictionaryConstLiteral();
+  listConstLiteral();
+  setConstLiteral();
+}
+
+mapConstLiteral() => const {0: 1};
+
+dictionaryConstLiteral() => const {'foo': 'bar'};
+
+listConstLiteral() => const ['foo', 'bar'];
+
+setConstLiteral() => const {'foo', 'bar'};