[vm/kernel] Enable kernel2kernel "constants" transformation in AOT mode (after running TFA)

This CL also uses the newer `onProblem` error reporting mechanism, which supports contexts.

The errors by the constant evaluator are formatted e.g. like this:

    .../language_2/compile_time_constant_o_test_01.dart:14:8: Error: Duplicate keys are not allowed in constant maps (found duplicate key "StringConstant(foo)").
      "foo": 499
       ^
    .../language_2/compile_time_constant_o_test_01.dart:32:24: Context: While analyzing:
      Expect.identical(m1, m3);

Change-Id: I463416e14686e218b0f08903bd6aa0bca7392260
Reviewed-on: https://dart-review.googlesource.com/53021
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes.dart b/pkg/front_end/lib/src/fasta/fasta_codes.dart
index 3d1368d..6e7dbc1 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes.dart
@@ -4,7 +4,7 @@
 
 library fasta.codes;
 
-import 'package:kernel/ast.dart' show DartType;
+import 'package:kernel/ast.dart' show Constant, DartType;
 
 import 'package:kernel/text/ast_to_text.dart' show NameSystem, Printer;
 
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index ae67c8e..df547c2 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -726,6 +726,257 @@
     tip: r"""Try removing either the 'const' keyword or the body.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeConstEvalContext = messageConstEvalContext;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageConstEvalContext =
+    const MessageCode("ConstEvalContext", message: r"""While analyzing:""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        Constant
+            constant)> templateConstEvalDuplicateKey = const Template<
+        Message Function(Constant constant)>(
+    messageTemplate:
+        r"""The key '#constant' conflicts with another existing key in the map.""",
+    withArguments: _withArgumentsConstEvalDuplicateKey);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant)> codeConstEvalDuplicateKey =
+    const Code<Message Function(Constant constant)>(
+        "ConstEvalDuplicateKey", templateConstEvalDuplicateKey,
+        analyzerCode: "EQUAL_KEYS_IN_MAP");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalDuplicateKey(Constant constant) {
+  return new Message(codeConstEvalDuplicateKey,
+      message:
+          """The key '$constant' conflicts with another existing key in the map.""",
+      arguments: {'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeConstEvalFailedAssertion = messageConstEvalFailedAssertion;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageConstEvalFailedAssertion = const MessageCode(
+    "ConstEvalFailedAssertion",
+    analyzerCode: "CONST_EVAL_THROWS_EXCEPTION",
+    message: r"""This assertion failed.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string)>
+    templateConstEvalFailedAssertionWithMessage =
+    const Template<Message Function(String string)>(
+        messageTemplate: r"""This assertion failed with message: #string""",
+        withArguments: _withArgumentsConstEvalFailedAssertionWithMessage);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)>
+    codeConstEvalFailedAssertionWithMessage =
+    const Code<Message Function(String string)>(
+        "ConstEvalFailedAssertionWithMessage",
+        templateConstEvalFailedAssertionWithMessage,
+        analyzerCode: "CONST_EVAL_THROWS_EXCEPTION");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalFailedAssertionWithMessage(String string) {
+  return new Message(codeConstEvalFailedAssertionWithMessage,
+      message: """This assertion failed with message: $string""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String string,
+        Constant constant,
+        DartType _type,
+        DartType
+            _type2)> templateConstEvalInvalidBinaryOperandType = const Template<
+        Message Function(String string, Constant constant, DartType _type,
+            DartType _type2)>(
+    messageTemplate:
+        r"""Binary operator '#string' on '#constant' requires operand of type '#type', but was of type '#type2'.""",
+    withArguments: _withArgumentsConstEvalInvalidBinaryOperandType);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(
+            String string, Constant constant, DartType _type, DartType _type2)>
+    codeConstEvalInvalidBinaryOperandType = const Code<
+        Message Function(
+            String string, Constant constant, DartType _type, DartType _type2)>(
+  "ConstEvalInvalidBinaryOperandType",
+  templateConstEvalInvalidBinaryOperandType,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidBinaryOperandType(
+    String string, Constant constant, DartType _type, DartType _type2) {
+  NameSystem nameSystem = new NameSystem();
+  StringBuffer buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type);
+  String type = '$buffer';
+
+  buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
+  String type2 = '$buffer';
+
+  return new Message(codeConstEvalInvalidBinaryOperandType,
+      message:
+          """Binary operator '$string' on '$constant' requires operand of type '$type', but was of type '$type2'.""",
+      arguments: {
+        'string': string,
+        'constant': constant,
+        'type': _type,
+        'type2': _type2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String string,
+        Constant
+            constant)> templateConstEvalInvalidMethodInvocation = const Template<
+        Message Function(String string, Constant constant)>(
+    messageTemplate:
+        r"""The method '#string' can't be invoked on '#constant' within a const context.""",
+    withArguments: _withArgumentsConstEvalInvalidMethodInvocation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, Constant constant)>
+    codeConstEvalInvalidMethodInvocation =
+    const Code<Message Function(String string, Constant constant)>(
+        "ConstEvalInvalidMethodInvocation",
+        templateConstEvalInvalidMethodInvocation,
+        analyzerCode: "UNDEFINED_OPERATOR");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidMethodInvocation(
+    String string, Constant constant) {
+  return new Message(codeConstEvalInvalidMethodInvocation,
+      message:
+          """The method '$string' can't be invoked on '$constant' within a const context.""",
+      arguments: {'string': string, 'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            name)> templateConstEvalInvalidStaticInvocation = const Template<
+        Message Function(String name)>(
+    messageTemplate:
+        r"""The invocation of '#name' is not allowed within a const context.""",
+    withArguments: _withArgumentsConstEvalInvalidStaticInvocation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeConstEvalInvalidStaticInvocation =
+    const Code<Message Function(String name)>(
+        "ConstEvalInvalidStaticInvocation",
+        templateConstEvalInvalidStaticInvocation,
+        analyzerCode: "CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidStaticInvocation(String name) {
+  return new Message(codeConstEvalInvalidStaticInvocation,
+      message:
+          """The invocation of '$name' is not allowed within a const context.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(Constant constant)>
+    templateConstEvalInvalidStringInterpolationOperand =
+    const Template<Message Function(Constant constant)>(
+        messageTemplate:
+            r"""The '#constant' can't be used as part of a string interpolation within a const context, only values of type 'null', 'bool', 'int', 'double', or 'String' can be used.""",
+        withArguments:
+            _withArgumentsConstEvalInvalidStringInterpolationOperand);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant)>
+    codeConstEvalInvalidStringInterpolationOperand =
+    const Code<Message Function(Constant constant)>(
+        "ConstEvalInvalidStringInterpolationOperand",
+        templateConstEvalInvalidStringInterpolationOperand,
+        analyzerCode: "CONST_EVAL_TYPE_BOOL_NUM_STRING");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidStringInterpolationOperand(
+    Constant constant) {
+  return new Message(codeConstEvalInvalidStringInterpolationOperand,
+      message:
+          """The '$constant' can't be used as part of a string interpolation within a const context, only values of type 'null', 'bool', 'int', 'double', or 'String' can be used.""",
+      arguments: {'constant': constant});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        Constant constant,
+        DartType _type,
+        DartType
+            _type2)> templateConstEvalInvalidType = const Template<
+        Message Function(Constant constant, DartType _type, DartType _type2)>(
+    messageTemplate:
+        r"""Expected constant '#constant' to be of type '#type', but was of type '#type2'.""",
+    withArguments: _withArgumentsConstEvalInvalidType);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(Constant constant, DartType _type, DartType _type2)>
+    codeConstEvalInvalidType = const Code<
+        Message Function(Constant constant, DartType _type, DartType _type2)>(
+  "ConstEvalInvalidType",
+  templateConstEvalInvalidType,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalInvalidType(
+    Constant constant, DartType _type, DartType _type2) {
+  NameSystem nameSystem = new NameSystem();
+  StringBuffer buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type);
+  String type = '$buffer';
+
+  buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
+  String type2 = '$buffer';
+
+  return new Message(codeConstEvalInvalidType,
+      message:
+          """Expected constant '$constant' to be of type '$type', but was of type '$type2'.""",
+      arguments: {'constant': constant, 'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateConstEvalNonConstantLiteral = const Template<
+        Message Function(String string)>(
+    messageTemplate:
+        r"""Can't have a non-constant #string literal within a const context.""",
+    withArguments: _withArgumentsConstEvalNonConstantLiteral);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codeConstEvalNonConstantLiteral =
+    const Code<Message Function(String string)>(
+        "ConstEvalNonConstantLiteral", templateConstEvalNonConstantLiteral,
+        analyzerCode: "NON_CONSTANT_DEFAULT_VALUE");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalNonConstantLiteral(String string) {
+  return new Message(codeConstEvalNonConstantLiteral,
+      message:
+          """Can't have a non-constant $string literal within a const context.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeConstFactory = messageConstFactory;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 692b8d5..5e79fc9 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -61,6 +61,26 @@
 ConstAndVar/script1: Fail
 ConstConstructorNonFinalField/analyzerCode: Fail
 ConstConstructorNonFinalField/example: Fail
+ConstEvalContext/analyzerCode: Fail # This is just used for displaying the context.
+ConstEvalContext/example: Fail # This is just used for displaying the context.
+ConstEvalDuplicateKey/dart2jsCode: Fail
+ConstEvalDuplicateKey/example: Fail
+ConstEvalFailedAssertion/dart2jsCode: Fail
+ConstEvalFailedAssertion/example: Fail
+ConstEvalFailedAssertionWithMessage/dart2jsCode: Fail
+ConstEvalFailedAssertionWithMessage/example: Fail
+ConstEvalInvalidBinaryOperandType/analyzerCode: Fail # CONST_EVAL_TYPE_NUM / CONST_EVAL_TYPE_BOOL
+ConstEvalInvalidBinaryOperandType/example: Fail
+ConstEvalInvalidMethodInvocation/dart2jsCode: Fail
+ConstEvalInvalidMethodInvocation/example: Fail
+ConstEvalInvalidStaticInvocation/dart2jsCode: Fail
+ConstEvalInvalidStaticInvocation/example: Fail
+ConstEvalInvalidStringInterpolationOperand/dart2jsCode: Fail
+ConstEvalInvalidStringInterpolationOperand/example: Fail
+ConstEvalInvalidType/analyzerCode: Fail # CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / ...
+ConstEvalInvalidType/example: Fail
+ConstEvalNonConstantLiteral/dart2jsCode: Fail
+ConstEvalNonConstantLiteral/example: Fail
 ConstFieldWithoutInitializer/example: Fail
 ConstructorHasNoSuchNamedParameter/analyzerCode: Fail
 ConstructorHasNoSuchNamedParameter/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 932840e..696070c 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -72,6 +72,43 @@
   analyzerCode: ILLEGAL_CHARACTER
   expression: "\x1b 1"
 
+ConstEvalContext:
+  template: "While analyzing:"
+
+ConstEvalDuplicateKey:
+  template: "The key '#constant' conflicts with another existing key in the map."
+  analyzerCode: EQUAL_KEYS_IN_MAP
+
+ConstEvalNonConstantLiteral:
+  template: "Can't have a non-constant #string literal within a const context."
+  analyzerCode: NON_CONSTANT_DEFAULT_VALUE
+
+ConstEvalInvalidType:
+  template: "Expected constant '#constant' to be of type '#type', but was of type '#type2'."
+
+ConstEvalInvalidBinaryOperandType:
+  template: "Binary operator '#string' on '#constant' requires operand of type '#type', but was of type '#type2'."
+
+ConstEvalInvalidMethodInvocation:
+  template: "The method '#string' can't be invoked on '#constant' within a const context."
+  analyzerCode: UNDEFINED_OPERATOR
+
+ConstEvalInvalidStringInterpolationOperand:
+  template: "The '#constant' can't be used as part of a string interpolation within a const context, only values of type 'null', 'bool', 'int', 'double', or 'String' can be used."
+  analyzerCode: CONST_EVAL_TYPE_BOOL_NUM_STRING
+
+ConstEvalInvalidStaticInvocation:
+  template: "The invocation of '#name' is not allowed within a const context."
+  analyzerCode: CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+ConstEvalFailedAssertion:
+  template: "This assertion failed."
+  analyzerCode: CONST_EVAL_THROWS_EXCEPTION
+
+ConstEvalFailedAssertionWithMessage:
+  template: "This assertion failed with message: #string"
+  analyzerCode: CONST_EVAL_THROWS_EXCEPTION
+
 NonAsciiIdentifier:
   template: "The non-ASCII character '#character' (#unicode) can't be used in identifiers, only in strings and comments."
   tip: "Try using an US-ASCII letter, a digit, '_' (an underscore), or '$' (a dollar sign)."
diff --git a/pkg/front_end/tool/_fasta/generate_messages.dart b/pkg/front_end/tool/_fasta/generate_messages.dart
index 0ec9a18..bae14ba 100644
--- a/pkg/front_end/tool/_fasta/generate_messages.dart
+++ b/pkg/front_end/tool/_fasta/generate_messages.dart
@@ -172,6 +172,11 @@
         arguments.add("'count2': count2");
         break;
 
+      case "#constant":
+        parameters.add("Constant constant");
+        arguments.add("'constant': constant");
+        break;
+
       default:
         throw "Unhandled placeholder in template: ${match[0]}";
     }
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index 2c55885..d44418f 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -409,9 +409,7 @@
   defaultTreeNode(Node node) {
     // Only a subset of the expression language is valid for constant
     // evaluation.
-    errorReporter.unimplemented(contextChain, node,
-        'Constant evaluation has no support for ${node.runtimeType} yet!');
-    throw const _AbortCurrentEvaluation();
+    throw 'Constant evaluation has no support for ${node.runtimeType} yet!';
   }
 
   visitNullLiteral(NullLiteral node) => nullConstant;
@@ -449,7 +447,7 @@
 
   visitListLiteral(ListLiteral node) {
     if (!node.isConst) {
-      errorReporter.nonConstantLiteral(contextChain, node, 'List');
+      errorReporter.nonConstLiteral(contextChain, node, 'List');
       throw const _AbortCurrentEvaluation();
     }
     final List<Constant> entries = new List<Constant>(node.expressions.length);
@@ -463,7 +461,7 @@
 
   visitMapLiteral(MapLiteral node) {
     if (!node.isConst) {
-      errorReporter.nonConstantLiteral(contextChain, node, 'Map');
+      errorReporter.nonConstLiteral(contextChain, node, 'Map');
       throw const _AbortCurrentEvaluation();
     }
     final Set<Constant> usedKeys = new Set<Constant>();
@@ -473,6 +471,9 @@
       final key = node.entries[i].key.accept(this);
       final value = node.entries[i].value.accept(this);
       if (!usedKeys.add(key)) {
+        // TODO(kustermann): We should change the context handling from just
+        // capturing the `TreeNode`s to a `(TreeNode, String message)` tuple and
+        // report where the first key with the same value was.
         errorReporter.duplicateKey(contextChain, node.entries[i], key);
         throw const _AbortCurrentEvaluation();
       }
@@ -485,23 +486,24 @@
     return canonicalize(backend.lowerMapConstant(mapConstant));
   }
 
+  visitFunctionExpression(FunctionExpression node) {
+    errorReporter.nonConstLiteral(contextChain, node, 'Function');
+    throw const _AbortCurrentEvaluation();
+  }
+
   visitConstructorInvocation(ConstructorInvocation node) {
     final Constructor constructor = node.target;
     final Class klass = constructor.enclosingClass;
     if (!constructor.isConst) {
-      errorReporter.nonConstConstructorInvocation(
-          contextChain, node, constructor);
-      throw const _AbortCurrentEvaluation();
+      throw 'The front-end should ensure we do not encounter a '
+          'constructor invocation of a non-const constructor.';
     }
-    if (constructor.function.body is! EmptyStatement) {
-      errorReporter.unreachable(contextChain, node,
-          'Constructor "$node" has non-trivial body "${constructor.function.body.runtimeType}".');
-      throw const _AbortCurrentEvaluation();
+    if (constructor.function.body != null &&
+        constructor.function.body is! EmptyStatement) {
+      throw 'Constructor "$node" has non-trivial body "${constructor.function.body.runtimeType}".';
     }
     if (klass.isAbstract) {
-      errorReporter.unreachable(contextChain, node,
-          'Constructor "$node" belongs to abstract class "${klass}".');
-      throw const _AbortCurrentEvaluation();
+      throw 'Constructor "$node" belongs to abstract class "${klass}".';
     }
 
     final typeArguments = evaluateTypeArguments(node.arguments);
@@ -587,20 +589,34 @@
               if (condition is BoolConstant) {
                 if (!condition.value) {
                   final Constant message = init.statement.message?.accept(this);
-                  errorReporter.failedAssertion(
-                      contextChain, init.statement.condition, message);
+                  if (message == null) {
+                    errorReporter.failedAssertion(
+                        contextChain, init.statement.condition, null);
+                    throw const _AbortCurrentEvaluation();
+                  } else if (message is StringConstant) {
+                    errorReporter.failedAssertion(
+                        contextChain, init.statement.condition, message.value);
+                    throw const _AbortCurrentEvaluation();
+                  }
+                  errorReporter.invalidDartType(
+                      contextChain,
+                      init.statement.message,
+                      message,
+                      typeEnvironment.stringType);
                   throw const _AbortCurrentEvaluation();
                 }
               } else {
-                errorReporter.invalidType(
-                    contextChain, init.statement.condition, condition, 'bool');
+                errorReporter.invalidDartType(
+                    contextChain,
+                    init.statement.condition,
+                    condition,
+                    typeEnvironment.boolType);
                 throw const _AbortCurrentEvaluation();
               }
             }
           } else {
-            errorReporter.unimplemented(contextChain, init,
+            throw new Exception(
                 'No support for handling initializer of type "${init.runtimeType}".');
-            throw const _AbortCurrentEvaluation();
           }
         }
       });
@@ -615,21 +631,16 @@
     final List<Constant> arguments =
         evaluatePositionalArguments(node.arguments);
 
+    // TODO(http://dartbug.com/31799): Ensure we only invoke ==/!= on
+    // null/bool/int/double/String objects.
+
     // Handle == and != first (it's common between all types).
     if (arguments.length == 1 && node.name.name == '==') {
-      // TODO(http://dartbug.com/31799): Re-enable these checks.
-      //ensurePrimitiveConstant(receiver);
       final right = arguments[0];
-      // TODO(http://dartbug.com/31799): Re-enable these checks.
-      //ensurePrimitiveConstant(right);
       return receiver == right ? trueConstant : falseConstant;
     }
     if (arguments.length == 1 && node.name.name == '!=') {
-      // TODO(http://dartbug.com/31799): Re-enable these checks.
-      //ensurePrimitiveConstant(receiver);
       final right = arguments[0];
-      // TODO(http://dartbug.com/31799): Re-enable these checks.
-      //ensurePrimitiveConstant(right);
       return receiver != right ? trueConstant : falseConstant;
     }
 
@@ -644,7 +655,12 @@
                   new StringConstant(receiver.value + other.value));
             }
             errorReporter.invalidBinaryOperandType(
-                contextChain, node, 'String', '+', 'String', '$other');
+                contextChain,
+                node,
+                receiver,
+                '+',
+                typeEnvironment.stringType,
+                other.getType(typeEnvironment));
             throw const _AbortCurrentEvaluation();
         }
       }
@@ -669,7 +685,12 @@
           }
         }
         errorReporter.invalidBinaryOperandType(
-            contextChain, node, 'bool', '${node.name.name}', 'bool', '$right');
+            contextChain,
+            node,
+            receiver,
+            '${node.name.name}',
+            typeEnvironment.boolType,
+            right.getType(typeEnvironment));
         throw const _AbortCurrentEvaluation();
       }
     } else if (receiver is IntConstant) {
@@ -710,8 +731,13 @@
               node.name.name, receiver.value, value, node);
         }
 
-        errorReporter.invalidBinaryOperandType(contextChain, node, 'int',
-            '${node.name.name}', 'int/double', '$other');
+        errorReporter.invalidBinaryOperandType(
+            contextChain,
+            node,
+            receiver,
+            '${node.name.name}',
+            typeEnvironment.numType,
+            other.getType(typeEnvironment));
         throw const _AbortCurrentEvaluation();
       }
     } else if (receiver is DoubleConstant) {
@@ -730,8 +756,13 @@
           return evaluateBinaryNumericOperation(
               node.name.name, receiver.value, value, node);
         }
-        errorReporter.invalidBinaryOperandType(contextChain, node, 'double',
-            '${node.name.name}', 'int/double', '$other');
+        errorReporter.invalidBinaryOperandType(
+            contextChain,
+            node,
+            receiver,
+            '${node.name.name}',
+            typeEnvironment.numType,
+            other.getType(typeEnvironment));
         throw const _AbortCurrentEvaluation();
       }
     }
@@ -752,7 +783,12 @@
             return right;
           }
           errorReporter.invalidBinaryOperandType(
-              contextChain, node, 'bool', '${node.operator}', 'bool', '$right');
+              contextChain,
+              node,
+              left,
+              '${node.operator}',
+              typeEnvironment.boolType,
+              right.getType(typeEnvironment));
           throw const _AbortCurrentEvaluation();
         }
         errorReporter.invalidMethodInvocation(
@@ -767,7 +803,12 @@
             return right;
           }
           errorReporter.invalidBinaryOperandType(
-              contextChain, node, 'bool', '${node.operator}', 'bool', '$right');
+              contextChain,
+              node,
+              left,
+              '${node.operator}',
+              typeEnvironment.boolType,
+              right.getType(typeEnvironment));
           throw const _AbortCurrentEvaluation();
         }
         errorReporter.invalidMethodInvocation(
@@ -789,7 +830,8 @@
     } else if (constant == falseConstant) {
       return evaluate(node.otherwise);
     } else {
-      errorReporter.invalidType(contextChain, node, constant, 'bool');
+      errorReporter.invalidDartType(
+          contextChain, node, constant, typeEnvironment.boolType);
       throw const _AbortCurrentEvaluation();
     }
   }
@@ -834,8 +876,8 @@
     if (!variable.isConst &&
         !_isFormalParameter(variable) &&
         variable.parent is! Let) {
-      errorReporter.nonConstantVariableGet(contextChain, node);
-      throw const _AbortCurrentEvaluation();
+      throw new Exception('The front-end should ensure we do not encounter a '
+          'variable get of a non-const variable.');
     }
     return env.lookupVariable(node.variable);
   }
@@ -854,9 +896,8 @@
         errorReporter.invalidStaticInvocation(contextChain, node, target);
         throw const _AbortCurrentEvaluation();
       } else {
-        errorReporter.unreachable(contextChain, node,
+        throw new Exception(
             'No support for ${target.runtimeType} in a static-get.');
-        throw const _AbortCurrentEvaluation();
       }
     });
   }
@@ -903,11 +944,7 @@
       if (parent is Library && parent == coreTypes.coreLibrary) {
         final positionalArguments = evaluatePositionalArguments(node.arguments);
         final Constant left = positionalArguments[0];
-        // TODO(http://dartbug.com/31799): Re-enable these checks.
-        //ensurePrimitiveConstant(left, node);
         final Constant right = positionalArguments[1];
-        // TODO(http://dartbug.com/31799): Re-enable these checks.
-        //ensurePrimitiveConstant(right, node);
         // Since we canonicalize constants during the evaluation, we can use
         // identical here.
         assert(left == right);
@@ -929,7 +966,8 @@
     if (constant is BoolConstant) {
       return constant == trueConstant ? falseConstant : trueConstant;
     }
-    errorReporter.invalidType(contextChain, node, constant, 'bool');
+    errorReporter.invalidDartType(
+        contextChain, node, constant, typeEnvironment.boolType);
     throw const _AbortCurrentEvaluation();
   }
 
@@ -946,16 +984,12 @@
         return canonicalize(
             new PartialInstantiationConstant(constant, node.typeArguments));
       }
-      errorReporter.unreachable(
-          contextChain,
-          node,
+      throw new Exception(
           'The number of type arguments supplied in the partial instantiation '
           'does not match the number of type arguments of the $constant.');
-      throw const _AbortCurrentEvaluation();
     }
-    errorReporter.unreachable(contextChain, node,
+    throw new Exception(
         'Only tear-off constants can be partially instantiated.');
-    throw const _AbortCurrentEvaluation();
   }
 
   // Helper methods:
@@ -985,13 +1019,11 @@
     } else if (constant is TypeLiteralConstant) {
       constantType = new InterfaceType(coreTypes.typeClass);
     } else {
-      errorReporter.unreachable(contextChain, node,
-          'No support for ${constant.runtimeType}.runtimeType');
-      throw const _AbortCurrentEvaluation();
+      throw new Exception('No support for ${constant.runtimeType}.runtimeType');
     }
 
     if (!typeEnvironment.isSubtypeOf(constantType, type)) {
-      errorReporter.invalidType(contextChain, node, constant, '$type');
+      errorReporter.invalidDartType(contextChain, node, constant, type);
       throw const _AbortCurrentEvaluation();
     }
   }
@@ -1015,7 +1047,7 @@
 
   List<Constant> evaluatePositionalArguments(Arguments arguments) {
     return arguments.positional.map((Expression node) {
-      return node.accept(this);
+      return node.accept(this) as Constant;
     }).toList();
   }
 
@@ -1053,18 +1085,6 @@
     }
   }
 
-  ensurePrimitiveConstant(Constant value, TreeNode node) {
-    if (value is! NullConstant &&
-        value is! BoolConstant &&
-        value is! IntConstant &&
-        value is! DoubleConstant &&
-        value is! StringConstant) {
-      errorReporter.invalidType(
-          contextChain, node, value, 'bool/int/double/Null/String');
-      throw const _AbortCurrentEvaluation();
-    }
-  }
-
   evaluateBinaryNumericOperation(String op, num a, num b, TreeNode node) {
     num result;
     switch (op) {
@@ -1105,8 +1125,7 @@
         return a > b ? trueConstant : falseConstant;
     }
 
-    errorReporter.invalidBinaryMethodInvocation(contextChain, node, op);
-    throw const _AbortCurrentEvaluation();
+    throw new Exception("Unexpected binary numeric operation '$op'.");
   }
 
   int _wrapAroundInteger(int value) {
@@ -1211,32 +1230,23 @@
 abstract class ErrorReporter {
   const ErrorReporter();
 
-  invalidType(List<TreeNode> context, TreeNode node, Constant receiver,
-      String expectedType);
+  invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+      DartType expectedType);
   invalidBinaryOperandType(List<TreeNode> context, TreeNode node,
-      String receiverType, String op, String expectedType, String actualType);
-  invalidBinaryMethodInvocation(
-      List<TreeNode> context, TreeNode node, String op);
+      Constant receiver, String op, DartType expectedType, DartType actualType);
   invalidMethodInvocation(
       List<TreeNode> context, TreeNode node, Constant receiver, String op);
   invalidStaticInvocation(
       List<TreeNode> context, TreeNode node, Procedure target);
   invalidStringInterpolationOperand(
       List<TreeNode> context, TreeNode node, Constant constant);
+  nonConstLiteral(List<TreeNode> context, TreeNode node, String klass);
   duplicateKey(List<TreeNode> context, TreeNode node, Constant key);
-  nonConstantLiteral(List<TreeNode> context, TreeNode node, String literalType);
-  nonConstantVariableGet(List<TreeNode> context, VariableGet node);
-  nonConstConstructorInvocation(
-      List<TreeNode> context, TreeNode node, Constructor target);
-  failedAssertion(List<TreeNode> context, TreeNode node, Constant message);
-  unimplemented(List<TreeNode> context, TreeNode node, String message);
-  unreachable(List<TreeNode> context, TreeNode node, String message);
+  failedAssertion(List<TreeNode> context, TreeNode node, String message);
 }
 
-abstract class ErrorReporterBase implements ErrorReporter {
-  const ErrorReporterBase();
-
-  report(List<TreeNode> context, String message, TreeNode node);
+class _SimpleErrorReporter implements ErrorReporter {
+  const _SimpleErrorReporter();
 
   getFileUri(TreeNode node) {
     while (node is! FileUriNode) {
@@ -1252,28 +1262,28 @@
     return node == null ? TreeNode.noOffset : node.fileOffset;
   }
 
-  invalidType(List<TreeNode> context, TreeNode node, Constant receiver,
-      String expectedType) {
+  invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+      DartType expectedType) {
     report(
         context,
         'Expected expression to evaluate to "$expectedType" but got "$receiver.',
         node);
   }
 
-  invalidBinaryOperandType(List<TreeNode> context, TreeNode node,
-      String receiverType, String op, String expectedType, String actualType) {
+  invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
     report(
         context,
-        'Calling "$op" on "$receiverType" needs operand of type '
+        'Calling "$op" on "$receiver" needs operand of type '
         '"$expectedType" (but got "$actualType")',
         node);
   }
 
-  invalidBinaryMethodInvocation(
-      List<TreeNode> context, TreeNode node, String op) {
-    report(context, 'Cannot call "$op" on a numeric constant receiver', node);
-  }
-
   invalidMethodInvocation(
       List<TreeNode> context, TreeNode node, Constant receiver, String op) {
     report(context, 'Cannot call "$op" on "$receiver" in constant expression',
@@ -1295,6 +1305,13 @@
         node);
   }
 
+  nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    report(
+        context,
+        'Cannot have a non-constant $klass literal within a const context.',
+        node);
+  }
+
   duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
     report(
         context,
@@ -1302,52 +1319,13 @@
         node);
   }
 
-  nonConstantLiteral(
-      List<TreeNode> context, TreeNode node, String literalType) {
-    report(
-        context,
-        '"$literalType" literals inside constant expressions are required to '
-        'be constant.',
-        node);
-  }
-
-  nonConstantVariableGet(List<TreeNode> context, VariableGet node) {
-    report(
-        context,
-        'The variable "${node.variable.name}" is non-const and '
-        'can therefore not be used inside a constant expression.'
-        ' (${node}, ${node.parent.parent}, )',
-        node);
-  }
-
-  nonConstConstructorInvocation(
-      List<TreeNode> context, TreeNode node, Constructor target) {
-    report(
-        context,
-        'The non-const constructor "$target" cannot be called inside a '
-        'constant expression',
-        node);
-  }
-
-  failedAssertion(List<TreeNode> context, TreeNode node, Constant message) {
+  failedAssertion(List<TreeNode> context, TreeNode node, String message) {
     report(
         context,
         'The assertion condition evaluated to "false" with message "$message"',
         node);
   }
 
-  unimplemented(List<TreeNode> context, TreeNode node, String message) {
-    report(context, 'Unimplemented: $message', node);
-  }
-
-  unreachable(List<TreeNode> context, TreeNode node, String message) {
-    report(context, 'Unreachable: $message', node);
-  }
-}
-
-class _SimpleErrorReporter extends ErrorReporterBase {
-  const _SimpleErrorReporter();
-
   report(List<TreeNode> context, String message, TreeNode node) {
     io.exitCode = 42;
     final Uri uri = getFileUri(node);
diff --git a/pkg/vm/bin/gen_kernel.dart b/pkg/vm/bin/gen_kernel.dart
index 0a2e4a2..8888c35 100644
--- a/pkg/vm/bin/gen_kernel.dart
+++ b/pkg/vm/bin/gen_kernel.dart
@@ -8,14 +8,14 @@
 import 'package:args/args.dart' show ArgParser, ArgResults;
 import 'package:front_end/src/api_prototype/front_end.dart';
 import 'package:kernel/binary/ast_to_binary.dart';
-import 'package:kernel/kernel.dart' show Component;
 import 'package:kernel/src/tool/batch_util.dart' as batch_util;
 import 'package:kernel/target/targets.dart' show TargetFlags;
 import 'package:kernel/target/vm.dart' show VmTarget;
 import 'package:kernel/text/ast_to_text.dart'
     show globalDebuggingNames, NameSystem;
 import 'package:vm/bytecode/gen_bytecode.dart' show isKernelBytecodeEnabled;
-import 'package:vm/kernel_front_end.dart' show compileToKernel, ErrorDetector;
+import 'package:vm/kernel_front_end.dart'
+    show compileToKernel, ErrorDetector, ErrorPrinter;
 
 final ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
   ..addOption('platform',
@@ -36,6 +36,14 @@
       help:
           'Enable global type flow analysis and related transformations in AOT mode.',
       defaultsTo: true)
+  ..addMultiOption('define',
+      abbr: 'D',
+      help: 'The values for the environment constants (e.g. -Dkey=value).')
+  ..addFlag('enable-asserts',
+      help: 'Whether asserts will be enabled.', defaultsTo: false)
+  ..addFlag('enable-constant-evaluation',
+      help: 'Whether kernel constant evaluation will be enabled.',
+      defaultsTo: true)
   ..addMultiOption('entry-points',
       help: 'Path to JSON file with the list of entry points')
   ..addFlag('gen-bytecode',
@@ -77,6 +85,13 @@
   final bool syncAsync = options['sync-async'];
   final bool tfa = options['tfa'];
   final bool genBytecode = options['gen-bytecode'];
+  final bool enableAsserts = options['enable-asserts'];
+  final bool enableConstantEvaluation = options['enable-constant-evaluation'];
+  final Map<String, String> environmentDefines = {};
+
+  if (!_parseDefines(options['define'], environmentDefines)) {
+    return _badUsageExitCode;
+  }
 
   final List<String> entryPoints = options['entry-points'] ?? <String>[];
   if (entryPoints.isEmpty) {
@@ -87,7 +102,8 @@
     ]);
   }
 
-  ErrorDetector errorDetector = new ErrorDetector();
+  final errorPrinter = new ErrorPrinter();
+  final errorDetector = new ErrorDetector(previousErrorHandler: errorPrinter);
 
   final CompilerOptions compilerOptions = new CompilerOptions()
     ..strongMode = strongMode
@@ -99,15 +115,21 @@
     ..packagesFileUri =
         packages != null ? Uri.base.resolveUri(new Uri.file(packages)) : null
     ..reportMessages = true
-    ..onError = errorDetector
+    ..onProblem = errorDetector
     ..embedSourceText = options['embed-sources'];
 
-  Component component = await compileToKernel(
-      Uri.base.resolveUri(new Uri.file(filename)), compilerOptions,
+  final inputUri = new Uri.file(filename);
+  final component = await compileToKernel(
+      Uri.base.resolveUri(inputUri), compilerOptions,
       aot: aot,
       useGlobalTypeFlowAnalysis: tfa,
       entryPoints: entryPoints,
-      genBytecode: genBytecode);
+      environmentDefines: environmentDefines,
+      genBytecode: genBytecode,
+      enableAsserts: enableAsserts,
+      enableConstantEvaluation: enableConstantEvaluation);
+
+  errorPrinter.printCompilationMessages(inputUri);
 
   if (errorDetector.hasCompilationErrors || (component == null)) {
     return _compileTimeErrorExitCode;
@@ -152,3 +174,22 @@
     }
   });
 }
+
+bool _parseDefines(
+    List<String> dFlags, Map<String, String> environmentDefines) {
+  for (final String dflag in dFlags) {
+    final equalsSignIndex = dflag.indexOf('=');
+    if (equalsSignIndex < 0) {
+      environmentDefines[dflag] = '';
+    } else if (equalsSignIndex > 0) {
+      final key = dflag.substring(0, equalsSignIndex);
+      final value = dflag.substring(equalsSignIndex + 1);
+      environmentDefines[key] = value;
+    } else {
+      print('The environment constant options must have a key (was: "$dflag")');
+      print(_usage);
+      return false;
+    }
+  }
+  return true;
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 8bf56e0..04521b1 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -7,16 +7,34 @@
 
 import 'dart:async';
 
+import 'package:front_end/src/base/processed_options.dart'
+    show ProcessedOptions;
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+import 'package:front_end/src/fasta/fasta_codes.dart' as codes;
+
 import 'package:front_end/src/api_prototype/compiler_options.dart'
-    show CompilerOptions, ErrorHandler;
+    show CompilerOptions, ProblemHandler;
 import 'package:front_end/src/api_prototype/kernel_generator.dart'
     show kernelForProgram;
 import 'package:front_end/src/api_prototype/compilation_message.dart'
-    show CompilationMessage, Severity;
-import 'package:front_end/src/fasta/severity.dart' show Severity;
-import 'package:kernel/ast.dart' show Component, StaticGet, Field;
+    show Severity;
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/ast.dart'
+    show
+        Component,
+        Constant,
+        DartType,
+        Field,
+        FileUriNode,
+        Procedure,
+        StaticGet,
+        TreeNode;
 import 'package:kernel/core_types.dart' show CoreTypes;
-import 'package:vm/bytecode/gen_bytecode.dart' show generateBytecode;
+import 'package:kernel/transformations/constants.dart' as constants;
+import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
+
+import 'bytecode/gen_bytecode.dart' show generateBytecode;
 
 import 'transformations/devirtualization.dart' as devirtualization
     show transformComponent;
@@ -36,23 +54,35 @@
     {bool aot: false,
     bool useGlobalTypeFlowAnalysis: false,
     List<String> entryPoints,
-    bool genBytecode: false}) async {
+    Map<String, String> environmentDefines,
+    bool genBytecode: false,
+    bool enableAsserts: false,
+    bool enableConstantEvaluation: true}) async {
   // Replace error handler to detect if there are compilation errors.
   final errorDetector =
-      new ErrorDetector(previousErrorHandler: options.onError);
-  options.onError = errorDetector;
+      new ErrorDetector(previousErrorHandler: options.onProblem);
+  options.onProblem = errorDetector;
 
   final component = await kernelForProgram(source, options);
 
-  // Restore error handler (in case 'options' are reused).
-  options.onError = errorDetector.previousErrorHandler;
-
   // Run global transformations only if component is correct.
-  if (aot && (component != null) && !errorDetector.hasCompilationErrors) {
-    _runGlobalTransformations(
-        component, options.strongMode, useGlobalTypeFlowAnalysis, entryPoints);
+  if (aot && component != null) {
+    await _runGlobalTransformations(
+        source,
+        options,
+        component,
+        options.strongMode,
+        useGlobalTypeFlowAnalysis,
+        entryPoints,
+        environmentDefines,
+        enableAsserts,
+        enableConstantEvaluation,
+        errorDetector);
   }
 
+  // Restore error handler (in case 'options' are reused).
+  options.onProblem = errorDetector.previousErrorHandler;
+
   if (genBytecode && component != null) {
     generateBytecode(component, strongMode: options.strongMode);
   }
@@ -60,11 +90,21 @@
   return component;
 }
 
-_runGlobalTransformations(Component component, bool strongMode,
-    bool useGlobalTypeFlowAnalysis, List<String> entryPoints) {
+Future _runGlobalTransformations(
+    Uri source,
+    CompilerOptions compilerOptions,
+    Component component,
+    bool strongMode,
+    bool useGlobalTypeFlowAnalysis,
+    List<String> entryPoints,
+    Map<String, String> environmentDefines,
+    bool enableAsserts,
+    bool enableConstantEvaluation,
+    ErrorDetector errorDetector) async {
   if (strongMode) {
-    final coreTypes = new CoreTypes(component);
+    if (errorDetector.hasCompilationErrors) return;
 
+    final coreTypes = new CoreTypes(component);
     _patchVmConstants(coreTypes);
 
     // TODO(alexmarkov, dmitryas): Consider doing canonicalization of identical
@@ -81,10 +121,56 @@
       devirtualization.transformComponent(coreTypes, component);
     }
 
+    if (enableConstantEvaluation) {
+      await _performConstantEvaluation(source, compilerOptions, component,
+          coreTypes, environmentDefines, strongMode, enableAsserts);
+
+      if (errorDetector.hasCompilationErrors) return;
+    }
+
     no_dynamic_invocations_annotator.transformComponent(component);
   }
 }
 
+Future _performConstantEvaluation(
+    Uri source,
+    CompilerOptions compilerOptions,
+    Component component,
+    CoreTypes coreTypes,
+    Map<String, String> environmentDefines,
+    bool strongMode,
+    bool enableAsserts) async {
+  final vmConstants =
+      new vm_constants.VmConstantsBackend(environmentDefines, coreTypes);
+
+  final processedOptions =
+      new ProcessedOptions(compilerOptions, false, [source]);
+
+  // Run within the context, so we have uri source tokens...
+  await CompilerContext.runWithOptions(processedOptions,
+      (CompilerContext context) async {
+    // To make the fileUri/fileOffset -> line/column mapping, we need to
+    // pre-fill the map.
+    context.uriToSource.addAll(component.uriToSource);
+
+    final hierarchy = new ClassHierarchy(component);
+    final typeEnvironment =
+        new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
+
+    // NOTE: Currently we keep fields, because there are certain constant
+    // fields which the VM accesses (e.g. `_Random._A` needs to be preserved).
+    // TODO(kustermann): We should use the entrypoints manifest to find out
+    // which fields need to be preserved and remove the rest.
+    constants.transformComponent(component, vmConstants,
+        keepFields: true,
+        strongMode: true,
+        evaluateAnnotations: false,
+        enableAsserts: enableAsserts,
+        errorReporter:
+            new ForwardConstantEvaluationErrors(context, typeEnvironment));
+  });
+}
+
 void _patchVmConstants(CoreTypes coreTypes) {
   // Fix Endian.host to be a const field equal to Endial.little instead of
   // a final field. VM does not support big-endian architectures at the
@@ -101,16 +187,147 @@
 }
 
 class ErrorDetector {
-  final ErrorHandler previousErrorHandler;
+  final ProblemHandler previousErrorHandler;
   bool hasCompilationErrors = false;
 
   ErrorDetector({this.previousErrorHandler});
 
-  void call(CompilationMessage message) {
-    if (message.severity == Severity.error) {
+  void call(codes.FormattedMessage problem, Severity severity,
+      List<codes.FormattedMessage> context) {
+    if (severity == Severity.error) {
       hasCompilationErrors = true;
     }
 
-    previousErrorHandler?.call(message);
+    previousErrorHandler?.call(problem, severity, context);
+  }
+}
+
+class ErrorPrinter {
+  final ProblemHandler previousErrorHandler;
+  final compilationMessages = <Uri, List<List>>{};
+
+  ErrorPrinter({this.previousErrorHandler});
+
+  void call(codes.FormattedMessage problem, Severity severity,
+      List<codes.FormattedMessage> context) {
+    final sourceUri = problem.locatedMessage.uri;
+    compilationMessages.putIfAbsent(sourceUri, () => [])
+      ..add([problem, context]);
+    previousErrorHandler?.call(problem, severity, context);
+  }
+
+  void printCompilationMessages(Uri baseUri) {
+    final sortedUris = compilationMessages.keys.toList()
+      ..sort((a, b) => '$a'.compareTo('$b'));
+    for (final Uri sourceUri in sortedUris) {
+      for (final List errorTuple in compilationMessages[sourceUri]) {
+        final codes.FormattedMessage message = errorTuple.first;
+        print(message.formatted);
+
+        final List context = errorTuple.last;
+        for (final codes.FormattedMessage message in context?.reversed) {
+          print(message.formatted);
+        }
+      }
+    }
+  }
+}
+
+class ForwardConstantEvaluationErrors implements constants.ErrorReporter {
+  final CompilerContext compilerContext;
+  final TypeEnvironment typeEnvironment;
+
+  ForwardConstantEvaluationErrors(this.compilerContext, this.typeEnvironment);
+
+  duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+    final message = codes.templateConstEvalDuplicateKey.withArguments(key);
+    reportIt(context, message, node);
+  }
+
+  invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+      DartType expectedType) {
+    final message = codes.templateConstEvalInvalidType.withArguments(
+        receiver, expectedType, receiver.getType(typeEnvironment));
+    reportIt(context, message, node);
+  }
+
+  invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
+    final message = codes.templateConstEvalInvalidBinaryOperandType
+        .withArguments(op, receiver, expectedType, actualType);
+    reportIt(context, message, node);
+  }
+
+  invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+    final message = codes.templateConstEvalInvalidMethodInvocation
+        .withArguments(op, receiver);
+    reportIt(context, message, node);
+  }
+
+  invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Procedure target) {
+    final message = codes.templateConstEvalInvalidStaticInvocation
+        .withArguments(target.name.toString());
+    reportIt(context, message, node);
+  }
+
+  invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    final message = codes.templateConstEvalInvalidStringInterpolationOperand
+        .withArguments(constant);
+    reportIt(context, message, node);
+  }
+
+  nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    final message =
+        codes.templateConstEvalNonConstantLiteral.withArguments(klass);
+    reportIt(context, message, node);
+  }
+
+  failedAssertion(List<TreeNode> context, TreeNode node, String string) {
+    final message = string == null
+        ? codes.messageConstEvalFailedAssertion
+        : codes.templateConstEvalFailedAssertionWithMessage
+            .withArguments(string);
+    reportIt(context, message, node);
+  }
+
+  reportIt(List<TreeNode> context, codes.Message message, TreeNode node) {
+    final Uri uri = getFileUri(node);
+    final int fileOffset = getFileOffset(node);
+
+    final contextMessages = <codes.LocatedMessage>[];
+    for (final TreeNode node in context) {
+      final Uri uri = getFileUri(node);
+      final int fileOffset = getFileOffset(node);
+      contextMessages.add(codes.messageConstEvalContext
+          .withLocation(uri, fileOffset, codes.noLength));
+    }
+
+    final locatedMessage =
+        message.withLocation(uri, fileOffset, codes.noLength);
+
+    compilerContext.options
+        .report(locatedMessage, Severity.error, context: contextMessages);
+  }
+
+  getFileUri(TreeNode node) {
+    while (node is! FileUriNode) {
+      node = node.parent;
+    }
+    return (node as FileUriNode).fileUri;
+  }
+
+  getFileOffset(TreeNode node) {
+    while (node.fileOffset == TreeNode.noOffset) {
+      node = node.parent;
+    }
+    return node == null ? TreeNode.noOffset : node.fileOffset;
   }
 }
diff --git a/pkg/vm/tool/precompiler2 b/pkg/vm/tool/precompiler2
index cd965fe..64908b5 100755
--- a/pkg/vm/tool/precompiler2
+++ b/pkg/vm/tool/precompiler2
@@ -24,10 +24,15 @@
     --packages=*)
     PACKAGES="$arg"
     ;;
+    --enable-asserts)
+    GEN_KERNEL_OPTIONS+=("$arg")
+    OPTIONS+=("$arg")
+    ;;
     --sync-async | \
     --no-sync-async | \
     --tfa | \
-    --no-tfa )
+    --no-tfa | \
+    -D* )
     GEN_KERNEL_OPTIONS+=("$arg")
     ;;
     --*)
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index a3b0e33..2d94185 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -2712,6 +2712,7 @@
     CollectImmediateSuperInterfaces(cls, &cids);
     RemoveCHAOptimizedCode(cls, cids);
   }
+
   if (cls.is_enum_class()) {
     AllocateEnumValues(cls);
   }
@@ -2765,11 +2766,15 @@
           (sentinel.raw() == field.raw())) {
         continue;
       }
-      field.SetStaticValue(Object::transition_sentinel());
-      result = Compiler::EvaluateStaticInitializer(field);
-      ASSERT(!result.IsError());
-      field.SetStaticValue(Instance::Cast(result), true);
-      field.RecordStore(Instance::Cast(result));
+      // The eager evaluation of the enum values is required for hot-reload (see
+      // commit e3ecc87).
+      if (!FLAG_precompiled_mode) {
+        field.SetStaticValue(Object::transition_sentinel());
+        result = Compiler::EvaluateStaticInitializer(field);
+        ASSERT(!result.IsError());
+        field.SetStaticValue(Instance::Cast(result), true);
+        field.RecordStore(Instance::Cast(result));
+      }
     }
   } else {
     const String& name_prefix =
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 6dc969f..2d3376a 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -68,11 +68,8 @@
 call_non_method_field_test/01: MissingCompileTimeError
 call_non_method_field_test/02: MissingCompileTimeError
 check_member_static_test/01: MissingCompileTimeError
-compile_time_constant_o_test/01: MissingCompileTimeError
-compile_time_constant_o_test/02: MissingCompileTimeError
 const_cast2_test/01: CompileTimeError
 const_cast2_test/none: CompileTimeError
-const_dynamic_type_literal_test/02: MissingCompileTimeError
 const_instance_field_test/01: MissingCompileTimeError # Fasta bug: Const instance field. Issue 32326.
 const_map2_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
 const_map3_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
@@ -104,10 +101,6 @@
 generic_methods_overriding_test/01: MissingCompileTimeError
 generic_methods_recursive_bound_test/02: MissingCompileTimeError
 getter_override_test/03: MissingCompileTimeError
-identical_const_test/01: MissingCompileTimeError
-identical_const_test/02: MissingCompileTimeError
-identical_const_test/03: MissingCompileTimeError
-identical_const_test/04: MissingCompileTimeError
 issue31596_override_test/07: MissingCompileTimeError
 issue31596_override_test/08: MissingCompileTimeError
 issue31596_super_test/02: MissingCompileTimeError
@@ -448,10 +441,15 @@
 [ $compiler != dart2js && $compiler != dartk && $compiler != dartkp && $fasta ]
 const_optional_args_test/01: MissingCompileTimeError
 
-[ $compiler != dart2js && $fasta ]
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dart2js && $compiler != dartkp && $fasta ]
 compile_time_constant_c_test/02: MissingCompileTimeError
 const_constructor_nonconst_field_test/01: MissingCompileTimeError
 const_syntax_test/05: MissingCompileTimeError
+
+[ $compiler != dart2js && $fasta ]
 mixin_super_2_test/01: MissingCompileTimeError
 mixin_super_2_test/03: MissingCompileTimeError
 mixin_supertype_subclass_test/02: MissingCompileTimeError
@@ -857,11 +855,6 @@
 checked_setter3_test/01: MissingCompileTimeError
 checked_setter3_test/02: MissingCompileTimeError
 checked_setter3_test/03: MissingCompileTimeError
-compile_time_constant_k_test/01: MissingCompileTimeError
-compile_time_constant_k_test/02: MissingCompileTimeError
-compile_time_constant_k_test/03: MissingCompileTimeError
-compile_time_constant_o_test/01: RuntimeError # KernelVM bug: Constant map duplicated key.
-compile_time_constant_o_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
 compile_time_constant_static5_test/11: CompileTimeError # Issue 31537
 compile_time_constant_static5_test/16: CompileTimeError # Issue 31537
 compile_time_constant_static5_test/21: CompileTimeError # Issue 31537
@@ -870,11 +863,9 @@
 conditional_import_test: CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
 config_import_corelib_test: CompileTimeError # Issue 31533
 config_import_test: RuntimeError # KernelVM bug: Configurable imports.
-const_dynamic_type_literal_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
 const_evaluation_test: SkipByDesign
 const_list_test: RuntimeError
 const_map4_test: RuntimeError
-const_nested_test: RuntimeError # KernelVM bug: Constant evaluation.
 constructor12_test: RuntimeError
 constructor3_test: Fail, OK, Pass
 ct_const2_test: Skip # Incompatible flag: --compile_all
@@ -974,8 +965,6 @@
 main_not_a_function_test: Skip
 main_test/03: RuntimeError
 many_overridden_no_such_method_test: SkipByDesign
-map_literal3_test/01: MissingCompileTimeError
-map_literal3_test/02: MissingCompileTimeError
 map_literal3_test/03: MissingCompileTimeError
 method_override4_test/01: MissingCompileTimeError
 method_override4_test/02: MissingCompileTimeError
@@ -1103,6 +1092,28 @@
 tearoff_dynamic_test: RuntimeError # Compares call to "foo" (noSuchMethod) with string "foo"
 type_variable_promotion_test: RuntimeError # Compares runtime type to the string "List<B>"
 
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dartkp && $fasta ]
+compile_time_constant_o_test/01: MissingCompileTimeError
+compile_time_constant_o_test/02: MissingCompileTimeError
+const_dynamic_type_literal_test/02: MissingCompileTimeError
+identical_const_test/01: MissingCompileTimeError
+identical_const_test/02: MissingCompileTimeError
+identical_const_test/03: MissingCompileTimeError
+identical_const_test/04: MissingCompileTimeError
+
+# The precomilation configuration uses a kernel2kernel constants evaluator
+# which is is more correct than fasta/vm in JIT mode (i.e. it catches more
+# compile-time errors).
+[ $compiler != dartkp && $fasta && $strong ]
+compile_time_constant_k_test/01: MissingCompileTimeError
+compile_time_constant_k_test/02: MissingCompileTimeError
+compile_time_constant_k_test/03: MissingCompileTimeError
+map_literal3_test/01: MissingCompileTimeError
+map_literal3_test/02: MissingCompileTimeError
+
 [ $compiler == fasta && $strong ]
 bad_override_test/03: MissingCompileTimeError
 check_member_static_test/02: MissingCompileTimeError
@@ -1296,16 +1307,11 @@
 variable_shadow_class_test/01: MissingCompileTimeError
 
 [ $fasta && $strong ]
-compile_time_constant_k_test/01: MissingCompileTimeError
-compile_time_constant_k_test/02: MissingCompileTimeError
-compile_time_constant_k_test/03: MissingCompileTimeError
 compile_time_constant_static2_test/04: MissingCompileTimeError
 compile_time_constant_static3_test/04: MissingCompileTimeError
 initializing_formal_type_annotation_test/01: MissingCompileTimeError
 initializing_formal_type_annotation_test/02: MissingCompileTimeError
 issue18628_2_test/01: MissingCompileTimeError
-map_literal3_test/01: MissingCompileTimeError
-map_literal3_test/02: MissingCompileTimeError
 map_literal3_test/03: MissingCompileTimeError
 redirecting_factory_infinite_steps_test/01: MissingCompileTimeError
 redirecting_factory_malbounded_test/01: MissingCompileTimeError
diff --git a/tools/testing/dart/compiler_configuration.dart b/tools/testing/dart/compiler_configuration.dart
index 4bc4061..37df80f 100644
--- a/tools/testing/dart/compiler_configuration.dart
+++ b/tools/testing/dart/compiler_configuration.dart
@@ -1062,6 +1062,8 @@
   bool get _useSdk;
   bool get _isStrong;
   bool get _isAot;
+  bool get _isChecked;
+  bool get _useEnableAsserts;
 
   String get executableScriptSuffix;
 
@@ -1107,6 +1109,10 @@
     }
 
     args.add(arguments.where((name) => name.endsWith('.dart')).single);
+    args.addAll(arguments.where((name) => name.startsWith('-D')));
+    if (_isChecked || _useEnableAsserts) {
+      args.add('--enable_asserts');
+    }
 
     // Pass environment variable to the gen_kernel script as
     // arguments are not passed if gen_kernel runs in batch mode.