Revert 5 changes with failures

TBR=askesc@google.com

Revert "[CFE] Move constant evaluator to Fasta."

This reverts commit 845b5b2df1c3ad1ce6ed75cc5f55ff3f8ec68cbf.

Revert "[CFE] Always call the constant evaluator by the evaluate method."

This reverts commit 91bc4ec2b94242594109987e33ea463eea95b5b4.

Revert "[CFE] Use Fasta diagnostics in the constant evaluator."

This reverts commit c7b572aa298e933252746a7b5332d77cbec4482e.

Revert "[CFE] Check for null in constant evaluation"

This reverts commit e6d2751e9cf52f655ef6694fbf02fda8c29ce00f.

Revert "Rename import after moving file."

This reverts commit a6e2c5eb4cce9915405d65529df507e8a2b474cc.

Change-Id: Iadfe087c0110f6f331b82d990213f95d3ef4541b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97223
Reviewed-by: William Hesse <whesse@google.com>
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 29361f8..0debe8b 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -10,6 +10,8 @@
 import 'package:kernel/core_types.dart';
 import 'package:kernel/class_hierarchy.dart';
 import 'package:kernel/target/targets.dart';
+import 'package:kernel/transformations/constants.dart'
+    show ConstantsBackend, NumberSemantics;
 import 'invocation_mirror_constants.dart';
 
 const Iterable<String> _allowedDartSchemePaths = const <String>[
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 2322008..a2cba73 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -3840,7 +3840,7 @@
   visitStaticGet(StaticGet node) {
     var target = node.target;
     if (target is Field && target.isConst) {
-      var value = _constants.evaluate(target.initializer);
+      var value = _constants.evaluate(target.initializer, cache: true);
       if (value is PrimitiveConstant) return value.accept(this);
     }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/constants.dart b/pkg/dev_compiler/lib/src/kernel/constants.dart
index 7f550cb..5f1e815 100644
--- a/pkg/dev_compiler/lib/src/kernel/constants.dart
+++ b/pkg/dev_compiler/lib/src/kernel/constants.dart
@@ -2,14 +2,9 @@
 // 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.
 
-// TODO(askesc): We should not need to call the constant evaluator
-// explicitly once constant-update-2018 is shipped.
-import 'package:front_end/src/api_prototype/constant_evaluator.dart'
-    show ConstantEvaluator, SimpleErrorReporter;
-
 import 'package:kernel/core_types.dart';
 import 'package:kernel/kernel.dart';
-import 'package:kernel/target/targets.dart';
+import 'package:kernel/transformations/constants.dart';
 import 'package:kernel/type_environment.dart';
 import 'kernel_helpers.dart';
 
@@ -34,11 +29,27 @@
   /// failed, or if the constant was unavailable.
   ///
   /// Returns [NullConstant] to represent the `null` value.
-  Constant evaluate(Expression e) {
+  ///
+  /// To avoid performance costs associated with try+catch on invalid constant
+  /// evaluation, call this after [isConstant] is known to be true.
+  Constant evaluate(Expression e, {bool cache = false}) {
     if (e == null) return null;
 
-    Constant result = _evaluator.evaluate(e);
-    return result is UnevaluatedConstant ? null : result;
+    try {
+      var result = cache ? _evaluator.evaluate(e) : e.accept(_evaluator);
+      return result is UnevaluatedConstant ? null : result;
+    } on _AbortCurrentEvaluation {
+      // TODO(jmesserly): the try+catch is necessary because the front end is
+      // not issuing sufficient errors, so the constant evaluation can fail.
+      //
+      // It can also be caused by methods in the evaluator that don't understand
+      // unavailable constants.
+      return null;
+    } on NoSuchMethodError {
+      // TODO(jmesserly): this is probably the same issue as above, but verify
+      // that it's fixed once Kernel does constant evaluation.
+      return null;
+    }
   }
 
   /// If [node] is an annotation with a field named `name`, returns that field's
@@ -168,7 +179,12 @@
 class _ErrorReporter extends SimpleErrorReporter {
   const _ErrorReporter();
 
-  // Ignore reported errors.
   @override
-  reportMessage(Uri uri, int offset, String message) {}
+  report(context, message, node) => throw const _AbortCurrentEvaluation();
+}
+
+// TODO(jmesserly): this class is private in Kernel constants library, so
+// we have our own version.
+class _AbortCurrentEvaluation {
+  const _AbortCurrentEvaluation();
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index 4b08093..db69224 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -8,6 +8,7 @@
 import 'package:kernel/core_types.dart';
 import 'package:kernel/class_hierarchy.dart';
 import 'package:kernel/target/targets.dart';
+import 'package:kernel/transformations/constants.dart' show ConstantsBackend;
 import 'constants.dart' show DevCompilerConstantsBackend;
 import 'kernel_helpers.dart';
 
diff --git a/pkg/front_end/lib/src/api_prototype/constant_evaluator.dart b/pkg/front_end/lib/src/api_prototype/constant_evaluator.dart
deleted file mode 100644
index 5eeb2a6..0000000
--- a/pkg/front_end/lib/src/api_prototype/constant_evaluator.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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.
-
-library front_end.constant_evaluator;
-
-export '../fasta/kernel/constant_evaluator.dart'
-    show
-        ConstantEvaluator,
-        ConstantsTransformer,
-        ErrorReporter,
-        EvaluationEnvironment,
-        SimpleErrorReporter,
-        transformComponent,
-        transformLibraries;
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 20ad6ad..e3ef443 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -34,6 +34,25 @@
 export '../fasta/fasta_codes.dart'
     show
         LocatedMessage,
+        Message,
+        messageConstEvalCircularity,
+        messageConstEvalContext,
+        messageConstEvalFailedAssertion,
+        noLength,
+        templateConstEvalFreeTypeParameter,
+        templateConstEvalDeferredLibrary,
+        templateConstEvalDuplicateKey,
+        templateConstEvalFailedAssertionWithMessage,
+        templateConstEvalInvalidBinaryOperandType,
+        templateConstEvalInvalidMethodInvocation,
+        templateConstEvalInvalidStaticInvocation,
+        templateConstEvalInvalidStringInterpolationOperand,
+        templateConstEvalInvalidSymbolName,
+        templateConstEvalInvalidType,
+        templateConstEvalNegativeShift,
+        templateConstEvalNonConstantLiteral,
+        templateConstEvalNonConstantVariableGet,
+        templateConstEvalZeroDivisor,
         templateFfiFieldAnnotation,
         templateFfiStructAnnotation,
         templateFfiNotStatic,
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 7dd32d8..574152c 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -1423,15 +1423,6 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeConstEvalNullValue = messageConstEvalNullValue;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageConstEvalNullValue = const MessageCode(
-    "ConstEvalNullValue",
-    analyzerCodes: <String>["CONST_EVAL_THROWS_EXCEPTION"],
-    message: r"""Null value during constant evaluation.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
         String string,
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
index 5f8b4f5..6cb0a71 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
@@ -4,28 +4,177 @@
 
 library fasta.kernel_constants;
 
-import 'package:kernel/ast.dart' show InvalidExpression, Library;
+import 'package:kernel/ast.dart'
+    show Constant, DartType, IntConstant, Library, Member, Procedure, TreeNode;
 
-import '../fasta_codes.dart' show LocatedMessage;
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
+
+import 'package:kernel/transformations/constants.dart' show ErrorReporter;
+
+import '../fasta_codes.dart'
+    show
+        Message,
+        noLength,
+        messageConstEvalCircularity,
+        messageConstEvalFailedAssertion,
+        templateConstEvalDeferredLibrary,
+        templateConstEvalDuplicateKey,
+        templateConstEvalFailedAssertionWithMessage,
+        templateConstEvalFreeTypeParameter,
+        templateConstEvalInvalidBinaryOperandType,
+        templateConstEvalInvalidMethodInvocation,
+        templateConstEvalInvalidStaticInvocation,
+        templateConstEvalInvalidStringInterpolationOperand,
+        templateConstEvalInvalidSymbolName,
+        templateConstEvalInvalidType,
+        templateConstEvalNegativeShift,
+        templateConstEvalNonConstantLiteral,
+        templateConstEvalNonConstantVariableGet,
+        templateConstEvalZeroDivisor;
 
 import '../loader.dart' show Loader;
 
-import 'constant_evaluator.dart' show ErrorReporter;
-
 class KernelConstantErrorReporter extends ErrorReporter {
   final Loader<Library> loader;
+  final TypeEnvironment typeEnvironment;
 
-  KernelConstantErrorReporter(this.loader);
+  KernelConstantErrorReporter(this.loader, this.typeEnvironment);
 
-  @override
-  void report(LocatedMessage message, List<LocatedMessage> context) {
-    loader.addProblem(
-        message.messageObject, message.charOffset, message.length, message.uri,
-        context: context);
+  String addProblem(TreeNode node, Message message) {
+    int offset = getFileOffset(node);
+    Uri uri = getFileUri(node);
+    loader.addProblem(message, offset, noLength, uri);
+    return loader.target.context.format(
+        message.withLocation(uri, offset, noLength), message.code.severity);
   }
 
   @override
-  void reportInvalidExpression(InvalidExpression node) {
-    // Assumed to be already reported.
+  String freeTypeParameter(
+      List<TreeNode> context, TreeNode node, DartType type) {
+    return addProblem(
+        node, templateConstEvalFreeTypeParameter.withArguments(type));
+  }
+
+  @override
+  String duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+    return addProblem(node, templateConstEvalDuplicateKey.withArguments(key));
+  }
+
+  @override
+  String invalidDartType(List<TreeNode> context, TreeNode node,
+      Constant receiver, DartType expectedType) {
+    return addProblem(
+        node,
+        templateConstEvalInvalidType.withArguments(
+            receiver, expectedType, receiver.getType(typeEnvironment)));
+  }
+
+  @override
+  String invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
+    return addProblem(
+        node,
+        templateConstEvalInvalidBinaryOperandType.withArguments(
+            op, receiver, expectedType, actualType));
+  }
+
+  @override
+  String invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+    return addProblem(node,
+        templateConstEvalInvalidMethodInvocation.withArguments(op, receiver));
+  }
+
+  @override
+  String invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Member target) {
+    // TODO(kmillikin) For an invalid factory invocation we should adopt a
+    // better message.  This will show something like:
+    //
+    // "The invocation of 'List' is not allowed within a const context."
+    //
+    // Which is not quite right when the code was "new List()".
+    String name = target.name.toString();
+    if (target is Procedure && target.isFactory) {
+      if (name.isEmpty) {
+        name = target.enclosingClass.name;
+      } else {
+        name = '${target.enclosingClass.name}.${name}';
+      }
+    }
+    return addProblem(
+        node, templateConstEvalInvalidStaticInvocation.withArguments(name));
+  }
+
+  @override
+  String invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    return addProblem(
+        node,
+        templateConstEvalInvalidStringInterpolationOperand
+            .withArguments(constant));
+  }
+
+  @override
+  String invalidSymbolName(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    return addProblem(
+        node, templateConstEvalInvalidSymbolName.withArguments(constant));
+  }
+
+  @override
+  String zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
+    return addProblem(node,
+        templateConstEvalZeroDivisor.withArguments(op, '${receiver.value}'));
+  }
+
+  @override
+  String negativeShift(List<TreeNode> context, TreeNode node,
+      IntConstant receiver, String op, IntConstant argument) {
+    return addProblem(
+        node,
+        templateConstEvalNegativeShift.withArguments(
+            op, '${receiver.value}', '${argument.value}'));
+  }
+
+  @override
+  String nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    return addProblem(
+        node, templateConstEvalNonConstantLiteral.withArguments(klass));
+  }
+
+  @override
+  String failedAssertion(List<TreeNode> context, TreeNode node, String string) {
+    return addProblem(
+        node,
+        (string == null)
+            ? messageConstEvalFailedAssertion
+            : templateConstEvalFailedAssertionWithMessage
+                .withArguments(string));
+  }
+
+  @override
+  String nonConstantVariableGet(
+      List<TreeNode> context, TreeNode node, String variableName) {
+    return addProblem(node,
+        templateConstEvalNonConstantVariableGet.withArguments(variableName));
+  }
+
+  @override
+  String deferredLibrary(
+      List<TreeNode> context, TreeNode node, String importName) {
+    return addProblem(
+        node, templateConstEvalDeferredLibrary.withArguments(importName));
+  }
+
+  @override
+  String circularity(List<TreeNode> context, TreeNode node) {
+    return addProblem(node, messageConstEvalCircularity);
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 5473197..a766167 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -43,6 +43,9 @@
 
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
 
+import 'package:kernel/transformations/constants.dart' as constants
+    show transformLibraries;
+
 import '../../api_prototype/file_system.dart' show FileSystem;
 
 import '../compiler_context.dart' show CompilerContext;
@@ -82,8 +85,6 @@
 
 import '../uri_translator.dart' show UriTranslator;
 
-import 'constant_evaluator.dart' as constants show transformLibraries;
-
 import 'kernel_builder.dart'
     show
         ClassBuilder,
@@ -758,7 +759,7 @@
           backendTarget.constantsBackend(loader.coreTypes),
           environmentDefines,
           environment,
-          new KernelConstantErrorReporter(loader),
+          new KernelConstantErrorReporter(loader, environment),
           enableAsserts: enableAsserts);
       ticker.logMs("Evaluated constants");
     }
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 88cbda7..cfa04f1 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -81,7 +81,6 @@
 ConstEvalNegativeShift/example: Fail
 ConstEvalNonConstantLiteral/example: Fail
 ConstEvalNonConstantVariableGet/example: Fail
-ConstEvalNullValue/example: Fail
 ConstEvalZeroDivisor/example: Fail
 ConstFieldWithoutInitializer/example: Fail
 ConstructorNotFound/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 304ad72..195f967 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -150,10 +150,6 @@
   template: "Constant expression depends on itself."
   analyzerCode: RECURSIVE_COMPILE_TIME_CONSTANT
 
-ConstEvalNullValue:
-  template: "Null value during constant evaluation."
-  analyzerCode: CONST_EVAL_THROWS_EXCEPTION
-
 NotConstantExpression:
   template: "#string is not a constant expression."
   analyzerCode: NOT_CONSTANT_EXPRESSION
diff --git a/pkg/kernel/bin/transform.dart b/pkg/kernel/bin/transform.dart
index d0019c3..5f792a5 100755
--- a/pkg/kernel/bin/transform.dart
+++ b/pkg/kernel/bin/transform.dart
@@ -6,9 +6,6 @@
 import 'dart:async';
 import 'dart:io';
 
-import 'package:front_end/src/api_prototype/constant_evaluator.dart'
-    as constants show SimpleErrorReporter, transformComponent;
-
 import 'package:args/args.dart';
 import 'package:kernel/class_hierarchy.dart';
 import 'package:kernel/core_types.dart';
@@ -16,6 +13,9 @@
 import 'package:kernel/src/tool/batch_util.dart';
 import 'package:kernel/target/targets.dart';
 
+import 'package:kernel/transformations/constants.dart' as constants
+    show SimpleErrorReporter, transformComponent;
+
 import 'package:kernel/transformations/continuation.dart' as cont;
 import 'package:kernel/transformations/empty.dart' as empty;
 import 'package:kernel/transformations/method_call.dart' as method_call;
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 47521ca..3472254 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -6,6 +6,7 @@
 import '../ast.dart';
 import '../class_hierarchy.dart';
 import '../core_types.dart';
+import '../transformations/constants.dart' show ConstantsBackend;
 
 final List<String> targetNames = targets.keys.toList();
 
@@ -32,32 +33,6 @@
       {List<C> context});
 }
 
-/// The different kinds of number semantics supported by the constant evaluator.
-enum NumberSemantics {
-  /// Dart VM number semantics.
-  vm,
-
-  /// JavaScript (Dart2js and DDC) number semantics.
-  js,
-}
-
-// Backend specific constant evaluation behavior
-class ConstantsBackend {
-  const ConstantsBackend();
-
-  /// Lowering of a list constant to a backend-specific representation.
-  Constant lowerListConstant(ListConstant constant) => constant;
-
-  /// Lowering of a set constant to a backend-specific representation.
-  Constant lowerSetConstant(SetConstant constant) => constant;
-
-  /// Lowering of a map constant to a backend-specific representation.
-  Constant lowerMapConstant(MapConstant constant) => constant;
-
-  /// Number semantics to use for this backend.
-  NumberSemantics get numberSemantics => NumberSemantics.vm;
-}
-
 /// A target provides backend-specific options for generating kernel IR.
 abstract class Target {
   String get name;
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/kernel/lib/transformations/constants.dart
similarity index 80%
rename from pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
rename to pkg/kernel/lib/transformations/constants.dart
index d202376..e7c77df 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -16,45 +16,17 @@
 /// Furthermore due to the lowering of certain constructs in the front-end
 /// (e.g. '??') we need to support a super-set of the normal constant expression
 /// language.  Issue(http://dartbug.com/31799)
-library fasta.constant_evaluator;
-
-import 'dart:core' hide MapEntry;
+library kernel.transformations.constants;
 
 import 'dart:io' as io;
 
-import 'package:kernel/ast.dart';
-import 'package:kernel/class_hierarchy.dart';
-import 'package:kernel/clone.dart';
-import 'package:kernel/core_types.dart';
-import 'package:kernel/kernel.dart';
-import 'package:kernel/type_algebra.dart';
-import 'package:kernel/type_environment.dart';
-
-import 'package:kernel/target/targets.dart';
-
-import '../fasta_codes.dart'
-    show
-        LocatedMessage,
-        Message,
-        messageConstEvalCircularity,
-        messageConstEvalContext,
-        messageConstEvalFailedAssertion,
-        messageConstEvalNullValue,
-        noLength,
-        templateConstEvalDeferredLibrary,
-        templateConstEvalDuplicateKey,
-        templateConstEvalFailedAssertionWithMessage,
-        templateConstEvalFreeTypeParameter,
-        templateConstEvalInvalidType,
-        templateConstEvalInvalidBinaryOperandType,
-        templateConstEvalInvalidMethodInvocation,
-        templateConstEvalInvalidStaticInvocation,
-        templateConstEvalInvalidStringInterpolationOperand,
-        templateConstEvalInvalidSymbolName,
-        templateConstEvalNegativeShift,
-        templateConstEvalNonConstantLiteral,
-        templateConstEvalNonConstantVariableGet,
-        templateConstEvalZeroDivisor;
+import '../ast.dart';
+import '../class_hierarchy.dart';
+import '../clone.dart';
+import '../core_types.dart';
+import '../kernel.dart';
+import '../type_algebra.dart';
+import '../type_environment.dart';
 
 Component transformComponent(Component component, ConstantsBackend backend,
     Map<String, String> environmentDefines, ErrorReporter errorReporter,
@@ -398,7 +370,7 @@
   }
 }
 
-class ConstantEvaluator extends RecursiveVisitor<Constant> {
+class ConstantEvaluator extends RecursiveVisitor {
   final ConstantsBackend backend;
   final NumberSemantics numberSemantics;
   Map<String, String> environmentDefines;
@@ -435,47 +407,13 @@
         nodeCache = <Node, Constant>{},
         env = new EvaluationEnvironment();
 
-  Uri getFileUri(TreeNode node) {
-    while (node != null && node is! FileUriNode) {
-      node = node.parent;
-    }
-    return (node as FileUriNode)?.fileUri;
-  }
-
-  int getFileOffset(Uri uri, TreeNode node) {
-    if (uri == null) return TreeNode.noOffset;
-    while (node != null && node.fileOffset == TreeNode.noOffset) {
-      node = node.parent;
-    }
-    return node == null ? TreeNode.noOffset : node.fileOffset;
-  }
-
-  /// Evaluate [node] and possibly cache the evaluation result.
-  /// Returns UnevaluatedConstant if the constant could not be evaluated.
-  /// If the expression in the UnevaluatedConstant is an InvalidExpression,
-  /// an error occurred during constant evaluation.
+  /// Evaluates [node] and possibly cache the evaluation result.
   Constant evaluate(Expression node) {
     evaluationRoot = node;
     try {
       return _evaluateSubexpression(node);
-    } on _AbortDueToError catch (e) {
-      final Uri uri = getFileUri(e.node);
-      final int fileOffset = getFileOffset(uri, e.node);
-      final locatedMessage = e.message.withLocation(uri, fileOffset, noLength);
-
-      final contextMessages = <LocatedMessage>[];
-      if (e.context != null) contextMessages.addAll(e.context);
-      for (final TreeNode node in contextChain) {
-        final Uri uri = getFileUri(node);
-        final int fileOffset = getFileOffset(uri, node);
-        contextMessages.add(
-            messageConstEvalContext.withLocation(uri, fileOffset, noLength));
-      }
-      errorReporter.report(locatedMessage, contextMessages);
-      return new UnevaluatedConstant(new InvalidExpression(e.message.message));
-    } on _AbortDueToInvalidExpression catch (e) {
-      errorReporter.reportInvalidExpression(e.node);
-      return new UnevaluatedConstant(e.node);
+    } on _AbortCurrentEvaluation catch (e) {
+      return new UnevaluatedConstant(new InvalidExpression(e.message));
     } finally {
       // Release collections used to keep track of unevaluated nodes.
       evaluationRoot = null;
@@ -484,14 +422,6 @@
     }
   }
 
-  Null report(TreeNode node, Message message, {List<LocatedMessage> context}) {
-    throw new _AbortDueToError(node, message, context: context);
-  }
-
-  Null reportInvalidExpression(InvalidExpression node) {
-    throw new _AbortDueToInvalidExpression(node);
-  }
-
   /// Produce an unevaluated constant node for an expression.
   /// Mark all ancestors (up to the root of the constant evaluation) to
   /// indicate that they should also be unevaluated.
@@ -525,16 +455,19 @@
     return unevaluatedNodes != null && unevaluatedNodes.contains(node);
   }
 
-  /// Evaluate [node] and possibly cache the evaluation result.
-  /// @throws _AbortDueToError or _AbortDueToInvalidExpression if expression
-  /// can't be evaluated.
+  /// Evaluates [node] and possibly cache the evaluation result.
+  /// @throws _AbortCurrentEvaluation if expression can't be evaluated.
   Constant _evaluateSubexpression(Expression node) {
     if (node == null) return nullConstant;
     if (env.isEmpty) {
       // We only try to evaluate the same [node] *once* within an empty
       // environment.
       if (nodeCache.containsKey(node)) {
-        return nodeCache[node] ?? report(node, messageConstEvalCircularity);
+        final Constant constant = nodeCache[node];
+        if (constant == null)
+          throw new _AbortCurrentEvaluation(
+              errorReporter.circularity(contextChain, node));
+        return constant;
       }
 
       nodeCache[node] = null;
@@ -610,8 +543,8 @@
 
   visitListLiteral(ListLiteral node) {
     if (!node.isConst) {
-      return report(
-          node, templateConstEvalNonConstantLiteral.withArguments('List'));
+      throw new _AbortCurrentEvaluation(
+          errorReporter.nonConstLiteral(contextChain, node, 'List'));
     }
     final List<Constant> entries = new List<Constant>(node.expressions.length);
     for (int i = 0; i < node.expressions.length; ++i) {
@@ -634,8 +567,8 @@
 
   visitSetLiteral(SetLiteral node) {
     if (!node.isConst) {
-      return report(
-          node, templateConstEvalNonConstantLiteral.withArguments('Set'));
+      throw new _AbortCurrentEvaluation(
+          errorReporter.nonConstLiteral(contextChain, node, 'Set'));
     }
     final List<Constant> entries = new List<Constant>(node.expressions.length);
     for (int i = 0; i < node.expressions.length; ++i) {
@@ -658,8 +591,8 @@
 
   visitMapLiteral(MapLiteral node) {
     if (!node.isConst) {
-      return report(
-          node, templateConstEvalNonConstantLiteral.withArguments('Map'));
+      throw new _AbortCurrentEvaluation(
+          errorReporter.nonConstLiteral(contextChain, node, 'Map'));
     }
     final Set<Constant> usedKeys = new Set<Constant>();
     final List<ConstantMapEntry> entries =
@@ -671,8 +604,8 @@
         // 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.
-        return report(
-            node.entries[i], templateConstEvalDuplicateKey.withArguments(key));
+        throw new _AbortCurrentEvaluation(
+            errorReporter.duplicateKey(contextChain, node.entries[i], key));
       }
       entries[i] = new ConstantMapEntry(key, value);
     }
@@ -694,8 +627,8 @@
   }
 
   visitFunctionExpression(FunctionExpression node) {
-    return report(
-        node, templateConstEvalNonConstantLiteral.withArguments('Function'));
+    throw new _AbortCurrentEvaluation(
+        errorReporter.nonConstLiteral(contextChain, node, 'Function'));
   }
 
   visitConstructorInvocation(ConstructorInvocation node) {
@@ -759,8 +692,8 @@
               isValidSymbolName(nameValue.value)) {
             return canonicalize(new SymbolConstant(nameValue.value, null));
           }
-          return report(node.arguments.positional.first,
-              templateConstEvalInvalidSymbolName.withArguments(nameValue));
+          throw new _AbortCurrentEvaluation(errorReporter.invalidSymbolName(
+              contextChain, node.arguments.positional.first, nameValue));
         }
 
         return canonicalize(result);
@@ -960,31 +893,30 @@
               if (condition is BoolConstant) {
                 if (!condition.value) {
                   if (init.statement.message == null) {
-                    return report(init.statement.condition,
-                        messageConstEvalFailedAssertion);
+                    throw new _AbortCurrentEvaluation(
+                        errorReporter.failedAssertion(
+                            contextChain, init.statement.condition, null));
                   }
                   final Constant message =
                       _evaluateSubexpression(init.statement.message);
                   if (message is StringConstant) {
-                    return report(
-                        init.statement.condition,
-                        templateConstEvalFailedAssertionWithMessage
-                            .withArguments(message.value));
+                    throw new _AbortCurrentEvaluation(
+                        errorReporter.failedAssertion(contextChain,
+                            init.statement.condition, message.value));
                   }
-                  return report(
-                      init.statement.message,
-                      templateConstEvalInvalidType.withArguments(
+                  throw new _AbortCurrentEvaluation(
+                      errorReporter.invalidDartType(
+                          contextChain,
+                          init.statement.message,
                           message,
-                          typeEnvironment.stringType,
-                          message.getType(typeEnvironment)));
+                          typeEnvironment.stringType));
                 }
               } else {
-                return report(
+                throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
+                    contextChain,
                     init.statement.condition,
-                    templateConstEvalInvalidType.withArguments(
-                        condition,
-                        typeEnvironment.boolType,
-                        condition.getType(typeEnvironment)));
+                    condition,
+                    typeEnvironment.boolType));
               }
             }
           } else {
@@ -997,7 +929,7 @@
   }
 
   visitInvalidExpression(InvalidExpression node) {
-    return reportInvalidExpression(node);
+    throw new _AbortCurrentEvaluation(node.message);
   }
 
   visitMethodInvocation(MethodInvocation node) {
@@ -1038,11 +970,12 @@
               return canonicalize(
                   new StringConstant(receiver.value + other.value));
             }
-            return report(
-                node,
-                templateConstEvalInvalidBinaryOperandType.withArguments(
-                    '+',
+            throw new _AbortCurrentEvaluation(
+                errorReporter.invalidBinaryOperandType(
+                    contextChain,
+                    node,
                     receiver,
+                    '+',
                     typeEnvironment.stringType,
                     other.getType(typeEnvironment)));
         }
@@ -1060,11 +993,12 @@
         final op = node.name.name;
         if (other is IntConstant) {
           if ((op == '<<' || op == '>>') && other.value < 0) {
-            return report(
+            throw new _AbortCurrentEvaluation(errorReporter.negativeShift(
+                contextChain,
                 node.arguments.positional.first,
-                // TODO(askesc): Change argument types in template to constants.
-                templateConstEvalNegativeShift.withArguments(
-                    op, '${receiver.value}', '${other.value}'));
+                receiver,
+                op,
+                other));
           }
           switch (op) {
             case '|':
@@ -1087,11 +1021,8 @@
 
         if (other is IntConstant) {
           if (other.value == 0 && (op == '%' || op == '~/')) {
-            return report(
-                node.arguments.positional.first,
-                // TODO(askesc): Change argument type in template to constant.
-                templateConstEvalZeroDivisor.withArguments(
-                    op, '${receiver.value}'));
+            throw new _AbortCurrentEvaluation(errorReporter.zeroDivisor(
+                contextChain, node.arguments.positional.first, receiver, op));
           }
 
           return evaluateBinaryNumericOperation(
@@ -1100,11 +1031,12 @@
           return evaluateBinaryNumericOperation(
               node.name.name, receiver.value, other.value, node);
         }
-        return report(
-            node,
-            templateConstEvalInvalidBinaryOperandType.withArguments(
-                node.name.name,
+        throw new _AbortCurrentEvaluation(
+            errorReporter.invalidBinaryOperandType(
+                contextChain,
+                node,
                 receiver,
+                '${node.name.name}',
                 typeEnvironment.numType,
                 other.getType(typeEnvironment)));
       }
@@ -1124,22 +1056,18 @@
           return evaluateBinaryNumericOperation(
               node.name.name, receiver.value, value, node);
         }
-        return report(
-            node,
-            templateConstEvalInvalidBinaryOperandType.withArguments(
-                node.name.name,
+        throw new _AbortCurrentEvaluation(
+            errorReporter.invalidBinaryOperandType(
+                contextChain,
+                node,
                 receiver,
+                '${node.name.name}',
                 typeEnvironment.numType,
                 other.getType(typeEnvironment)));
       }
-    } else if (receiver is NullConstant) {
-      return report(node, messageConstEvalNullValue);
     }
-
-    return report(
-        node,
-        templateConstEvalInvalidMethodInvocation.withArguments(
-            node.name.name, receiver));
+    throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
+        contextChain, node, receiver, node.name.name));
   }
 
   visitLogicalExpression(LogicalExpression node) {
@@ -1160,18 +1088,17 @@
             return right;
           }
 
-          return report(
-              node,
-              templateConstEvalInvalidBinaryOperandType.withArguments(
-                  node.operator,
+          throw new _AbortCurrentEvaluation(
+              errorReporter.invalidBinaryOperandType(
+                  contextChain,
+                  node,
                   left,
+                  '${node.operator}',
                   typeEnvironment.boolType,
                   right.getType(typeEnvironment)));
         }
-        return report(
-            node,
-            templateConstEvalInvalidMethodInvocation.withArguments(
-                node.operator, left));
+        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
+            contextChain, node, left, '${node.operator}'));
       case '&&':
         if (left is BoolConstant) {
           if (!left.value) return falseConstant;
@@ -1181,27 +1108,24 @@
             return right;
           }
 
-          return report(
-              node,
-              templateConstEvalInvalidBinaryOperandType.withArguments(
-                  node.operator,
+          throw new _AbortCurrentEvaluation(
+              errorReporter.invalidBinaryOperandType(
+                  contextChain,
+                  node,
                   left,
+                  '${node.operator}',
                   typeEnvironment.boolType,
                   right.getType(typeEnvironment)));
         }
-        return report(
-            node,
-            templateConstEvalInvalidMethodInvocation.withArguments(
-                node.operator, left));
+        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
+            contextChain, node, left, '${node.operator}'));
       case '??':
         return (left is! NullConstant)
             ? left
             : _evaluateSubexpression(node.right);
       default:
-        return report(
-            node,
-            templateConstEvalInvalidMethodInvocation.withArguments(
-                node.operator, left));
+        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
+            contextChain, node, left, '${node.operator}'));
     }
   }
 
@@ -1220,10 +1144,8 @@
               cloner.clone(node.otherwise),
               node.staticType));
     } else {
-      return report(
-          node,
-          templateConstEvalInvalidType.withArguments(condition,
-              typeEnvironment.boolType, condition.getType(typeEnvironment)));
+      throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
+          contextChain, node, condition, typeEnvironment.boolType));
     }
   }
 
@@ -1252,8 +1174,6 @@
           node,
           new PropertyGet(
               unique(receiver.expression), node.name, node.interfaceTarget));
-    } else if (receiver is NullConstant) {
-      return report(node, messageConstEvalNullValue);
     }
     throw 'Could not evaluate property get on $receiver.';
   }
@@ -1273,11 +1193,12 @@
     // variables might allow more than it should.
     final VariableDeclaration variable = node.variable;
     if (variable.parent is Let || _isFormalParameter(variable)) {
-      return env.lookupVariable(node.variable) ??
-          report(
-              node,
-              templateConstEvalNonConstantVariableGet
-                  .withArguments(variable.name));
+      final Constant constant = env.lookupVariable(node.variable);
+      if (constant == null) {
+        throw new _AbortCurrentEvaluation(errorReporter.nonConstantVariableGet(
+            contextChain, node, variable.name));
+      }
+      return constant;
     }
     if (variable.isConst) {
       return _evaluateSubexpression(variable.initializer);
@@ -1299,18 +1220,14 @@
             return _evaluateSubexpression(target.initializer);
           });
         }
-        return report(
-            node,
-            templateConstEvalInvalidStaticInvocation
-                .withArguments(target.name.name));
+        throw new _AbortCurrentEvaluation(
+            errorReporter.invalidStaticInvocation(contextChain, node, target));
       } else if (target is Procedure) {
         if (target.kind == ProcedureKind.Method) {
           return canonicalize(new TearOffConstant(target));
         }
-        return report(
-            node,
-            templateConstEvalInvalidStaticInvocation
-                .withArguments(target.name.name));
+        throw new _AbortCurrentEvaluation(
+            errorReporter.invalidStaticInvocation(contextChain, node, target));
       } else {
         throw new Exception(
             'No support for ${target.runtimeType} in a static-get.');
@@ -1322,7 +1239,7 @@
     final List<Object> concatenated = <Object>[new StringBuffer()];
     for (int i = 0; i < node.expressions.length; i++) {
       Constant constant = _evaluateSubexpression(node.expressions[i]);
-      if (constant is PrimitiveConstant<Object>) {
+      if (constant is PrimitiveConstant) {
         String value = constant.value.toString();
         Object last = concatenated.last;
         if (last is StringBuffer) {
@@ -1333,10 +1250,8 @@
       } else if (constant is UnevaluatedConstant) {
         concatenated.add(constant);
       } else {
-        return report(
-            node,
-            templateConstEvalInvalidStringInterpolationOperand
-                .withArguments(constant));
+        throw new _AbortCurrentEvaluation(errorReporter
+            .invalidStringInterpolationOperand(contextChain, node, constant));
       }
     }
     if (concatenated.length > 1) {
@@ -1402,9 +1317,8 @@
               if (value == null) return nullConstant;
               return canonicalize(new StringConstant(value));
             }
-          } else if (name is NullConstant) {
-            return report(node, messageConstEvalNullValue);
           }
+          // TODO(askesc): Give more meaningful error message if name is null.
         } else {
           // Leave environment constant unevaluated.
           return unevaluated(
@@ -1425,23 +1339,8 @@
         return identical(left, right) ? trueConstant : falseConstant;
       }
     }
-
-    // TODO(kmillikin) For an invalid factory invocation we should adopt a
-    // better message.  This will show something like:
-    //
-    // "The invocation of 'List' is not allowed within a const context."
-    //
-    // Which is not quite right when the code was "new List()".
-    String name = target.name.name;
-    if (target is Procedure && target.isFactory) {
-      if (name.isEmpty) {
-        name = target.enclosingClass.name;
-      } else {
-        name = '${target.enclosingClass.name}.${name}';
-      }
-    }
-    return report(
-        node, templateConstEvalInvalidStaticInvocation.withArguments(name));
+    throw new _AbortCurrentEvaluation(
+        errorReporter.invalidStaticInvocation(contextChain, node, target));
   }
 
   visitAsExpression(AsExpression node) {
@@ -1462,10 +1361,8 @@
     if (constant is UnevaluatedConstant) {
       return unevaluated(node, new Not(unique(constant.expression)));
     }
-    return report(
-        node,
-        templateConstEvalInvalidType.withArguments(constant,
-            typeEnvironment.boolType, constant.getType(typeEnvironment)));
+    throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
+        contextChain, node, constant, typeEnvironment.boolType));
   }
 
   visitSymbolLiteral(SymbolLiteral node) {
@@ -1491,16 +1388,14 @@
       return unevaluated(node,
           new Instantiation(unique(constant.expression), node.typeArguments));
     }
-    // The inner expression in an instantiation can never be null, since
-    // instantiations are only inferred on direct references to declarations.
     throw new Exception(
         'Only tear-off constants can be partially instantiated.');
   }
 
   @override
   visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
-    return report(
-        node, templateConstEvalDeferredLibrary.withArguments(node.import.name));
+    throw new _AbortCurrentEvaluation(
+        errorReporter.deferredLibrary(contextChain, node, node.import.name));
   }
 
   // Helper methods:
@@ -1521,10 +1416,8 @@
     DartType constantType = constant.getType(typeEnvironment);
 
     if (!typeEnvironment.isSubtypeOf(constantType, type)) {
-      return report(
-          node,
-          templateConstEvalInvalidType.withArguments(
-              constant, type, constant.getType(typeEnvironment)));
+      throw new _AbortCurrentEvaluation(
+          errorReporter.invalidDartType(contextChain, node, constant, type));
     }
   }
 
@@ -1547,8 +1440,8 @@
     final result = env.subsituteType(type);
 
     if (!isInstantiated(result)) {
-      return report(
-          node, templateConstEvalFreeTypeParameter.withArguments(type));
+      throw new _AbortCurrentEvaluation(
+          errorReporter.freeTypeParameter(contextChain, node, type));
     }
 
     return result;
@@ -1736,52 +1629,228 @@
   }
 }
 
-// Used as control-flow to abort the current evaluation.
-class _AbortDueToError {
-  final TreeNode node;
-  final Message message;
-  final List<LocatedMessage> context;
+/// The different kinds of number semantics supported by the constant evaluator.
+enum NumberSemantics {
+  /// Dart VM number semantics.
+  vm,
 
-  _AbortDueToError(this.node, this.message, {this.context});
+  /// JavaScript (Dart2js and DDC) number semantics.
+  js,
 }
 
-class _AbortDueToInvalidExpression {
-  final InvalidExpression node;
+// Backend specific constant evaluation behavior
+class ConstantsBackend {
+  const ConstantsBackend();
 
-  _AbortDueToInvalidExpression(this.node);
+  /// Lowering of a list constant to a backend-specific representation.
+  Constant lowerListConstant(ListConstant constant) => constant;
+
+  /// Lowering of a set constant to a backend-specific representation.
+  Constant lowerSetConstant(SetConstant constant) => constant;
+
+  /// Lowering of a map constant to a backend-specific representation.
+  Constant lowerMapConstant(MapConstant constant) => constant;
+
+  /// Number semantics to use for this backend.
+  NumberSemantics get numberSemantics => NumberSemantics.vm;
+}
+
+// Used as control-flow to abort the current evaluation.
+class _AbortCurrentEvaluation {
+  final String message;
+  _AbortCurrentEvaluation(this.message);
 }
 
 abstract class ErrorReporter {
   const ErrorReporter();
 
-  void report(LocatedMessage message, List<LocatedMessage> context);
+  Uri getFileUri(TreeNode node) {
+    while (node is! FileUriNode) {
+      node = node.parent;
+    }
+    return (node as FileUriNode).fileUri;
+  }
 
-  void reportInvalidExpression(InvalidExpression node);
+  int getFileOffset(TreeNode node) {
+    while (node.fileOffset == TreeNode.noOffset) {
+      node = node.parent;
+    }
+    return node == null ? TreeNode.noOffset : node.fileOffset;
+  }
+
+  String freeTypeParameter(
+      List<TreeNode> context, TreeNode node, DartType type);
+  String invalidDartType(List<TreeNode> context, TreeNode node,
+      Constant receiver, DartType expectedType);
+  String invalidBinaryOperandType(List<TreeNode> context, TreeNode node,
+      Constant receiver, String op, DartType expectedType, DartType actualType);
+  String invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op);
+  String invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Member target);
+  String invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant);
+  String invalidSymbolName(
+      List<TreeNode> context, TreeNode node, Constant constant);
+  String zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op);
+  String negativeShift(List<TreeNode> context, TreeNode node,
+      IntConstant receiver, String op, IntConstant argument);
+  String nonConstLiteral(List<TreeNode> context, TreeNode node, String klass);
+  String duplicateKey(List<TreeNode> context, TreeNode node, Constant key);
+  String failedAssertion(List<TreeNode> context, TreeNode node, String message);
+  String nonConstantVariableGet(
+      List<TreeNode> context, TreeNode node, String variableName);
+  String deferredLibrary(
+      List<TreeNode> context, TreeNode node, String importName);
+  String circularity(List<TreeNode> context, TreeNode node);
 }
 
-class SimpleErrorReporter implements ErrorReporter {
+class SimpleErrorReporter extends ErrorReporter {
   const SimpleErrorReporter();
 
-  @override
-  void report(LocatedMessage message, List<LocatedMessage> context) {
-    _report(message);
-    for (LocatedMessage contextMessage in context) {
-      _report(contextMessage);
-    }
-  }
-
-  @override
-  void reportInvalidExpression(InvalidExpression node) {
-    // Ignored
-  }
-
-  void _report(LocatedMessage message) {
-    reportMessage(message.uri, message.charOffset, message.message);
-  }
-
-  void reportMessage(Uri uri, int offset, String message) {
+  String report(List<TreeNode> context, String what, TreeNode node) {
     io.exitCode = 42;
-    io.stderr.writeln('$uri:$offset Constant evaluation error: $message');
+    final Uri uri = getFileUri(node);
+    final int fileOffset = getFileOffset(node);
+    final String message = '$uri:$fileOffset Constant evaluation error: $what';
+    io.stderr.writeln(message);
+    return message;
+  }
+
+  @override
+  String freeTypeParameter(
+      List<TreeNode> context, TreeNode node, DartType type) {
+    return report(
+        context, 'Expected type to be instantiated but was ${type}', node);
+  }
+
+  @override
+  String invalidDartType(List<TreeNode> context, TreeNode node,
+      Constant receiver, DartType expectedType) {
+    return report(
+        context,
+        'Expected expression to evaluate to "$expectedType" but got "$receiver.',
+        node);
+  }
+
+  @override
+  String invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
+    return report(
+        context,
+        'Calling "$op" on "$receiver" needs operand of type '
+        '"$expectedType" (but got "$actualType")',
+        node);
+  }
+
+  @override
+  String invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+    return report(context,
+        'Cannot call "$op" on "$receiver" in constant expression', node);
+  }
+
+  @override
+  String invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Member target) {
+    return report(
+        context, 'Cannot invoke "$target" inside a constant expression', node);
+  }
+
+  @override
+  String invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    return report(
+        context,
+        'Only null/bool/int/double/String values are allowed as string '
+        'interpolation expressions during constant evaluation (was: "$constant").',
+        node);
+  }
+
+  @override
+  String invalidSymbolName(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    return report(
+        context,
+        'The symbol name must be a valid public Dart member name, public '
+        'constructor name, or library name, optionally qualified.',
+        node);
+  }
+
+  @override
+  String zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
+    return report(
+        context,
+        "Binary operator '$op' on '${receiver.value}' requires non-zero "
+        "divisor, but divisor was '0'.",
+        node);
+  }
+
+  @override
+  String negativeShift(List<TreeNode> context, TreeNode node,
+      IntConstant receiver, String op, IntConstant argument) {
+    return report(
+        context,
+        "Binary operator '$op' on '${receiver.value}' requires non-negative "
+        "operand, but was '${argument.value}'.",
+        node);
+  }
+
+  @override
+  String nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    return report(
+        context,
+        'Cannot have a non-constant $klass literal within a const context.',
+        node);
+  }
+
+  @override
+  String duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+    return report(
+        context,
+        'Duplicate keys are not allowed in constant maps (found duplicate key "$key")',
+        node);
+  }
+
+  @override
+  String failedAssertion(
+      List<TreeNode> context, TreeNode node, String message) {
+    return report(
+        context,
+        'The assertion condition evaluated to "false" with message "$message"',
+        node);
+  }
+
+  @override
+  String nonConstantVariableGet(
+      List<TreeNode> context, TreeNode node, String variableName) {
+    return report(
+        context,
+        'The variable "$variableName" cannot be used inside a constant '
+        'expression.',
+        node);
+  }
+
+  @override
+  String deferredLibrary(
+      List<TreeNode> context, TreeNode node, String importName) {
+    return report(
+        context,
+        'Deferred "$importName" cannot be used inside a constant '
+        'expression',
+        node);
+  }
+
+  @override
+  String circularity(List<TreeNode> context, TreeNode node) {
+    return report(context, 'Constant expression depends on itself.', node);
   }
 }
 
diff --git a/pkg/kernel/lib/vm/constants_native_effects.dart b/pkg/kernel/lib/vm/constants_native_effects.dart
index 2dc607d..9a9c9c8 100644
--- a/pkg/kernel/lib/vm/constants_native_effects.dart
+++ b/pkg/kernel/lib/vm/constants_native_effects.dart
@@ -5,7 +5,7 @@
 library vm.constants_native_effects;
 
 import '../ast.dart';
-import '../target/targets.dart';
+import '../transformations/constants.dart';
 import '../core_types.dart';
 
 class VmConstantsBackend extends ConstantsBackend {
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 3e69e5a..cecadf5 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -4,17 +4,17 @@
 
 library vm.bytecode.gen_bytecode;
 
-// TODO(askesc): We should not need to call the constant evaluator
-// explicitly once constant-update-2018 is shipped.
-import 'package:front_end/src/api_prototype/constant_evaluator.dart'
-    show ConstantEvaluator, EvaluationEnvironment, ErrorReporter;
-
 import 'package:kernel/ast.dart' hide MapEntry;
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 import 'package:kernel/core_types.dart' show CoreTypes;
 import 'package:kernel/external_name.dart' show getExternalName;
 import 'package:kernel/library_index.dart' show LibraryIndex;
-import 'package:kernel/target/targets.dart' show ConstantsBackend;
+import 'package:kernel/transformations/constants.dart'
+    show
+        ConstantEvaluator,
+        ConstantsBackend,
+        EvaluationEnvironment,
+        ErrorReporter;
 import 'package:kernel/type_algebra.dart'
     show Substitution, containsTypeVariable;
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
@@ -58,7 +58,7 @@
       onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
   final typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
   final constantsBackend = new VmConstantsBackend(coreTypes);
-  final errorReporter = new ForwardConstantEvaluationErrors();
+  final errorReporter = new ForwardConstantEvaluationErrors(typeEnvironment);
   libraries ??= component.libraries;
   final bytecodeGenerator = new BytecodeGenerator(
       component,
diff --git a/pkg/vm/lib/constants_error_reporter.dart b/pkg/vm/lib/constants_error_reporter.dart
index 724f5d8..d2fdc95 100644
--- a/pkg/vm/lib/constants_error_reporter.dart
+++ b/pkg/vm/lib/constants_error_reporter.dart
@@ -6,26 +6,164 @@
 /// [constants.ErrorReporter] which uses package:front_end to report errors.
 library vm.constants_error_reporter;
 
-import 'package:front_end/src/api_prototype/constant_evaluator.dart'
-    as constants;
-
 import 'package:front_end/src/api_unstable/vm.dart'
-    show CompilerContext, LocatedMessage, Severity;
+    show CompilerContext, Severity;
 
-import 'package:kernel/ast.dart' show InvalidExpression;
+import 'package:front_end/src/api_unstable/vm.dart' as codes;
 
-class ForwardConstantEvaluationErrors implements constants.ErrorReporter {
+import 'package:kernel/ast.dart'
+    show Constant, DartType, IntConstant, Member, TreeNode;
+import 'package:kernel/transformations/constants.dart' as constants;
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
+
+class ForwardConstantEvaluationErrors extends constants.ErrorReporter {
   // This will get the currently active [CompilerContext] from a zone variable.
   // If there is no active context, this will throw.
   final CompilerContext compilerContext = CompilerContext.current;
 
+  final TypeEnvironment typeEnvironment;
+
+  ForwardConstantEvaluationErrors(this.typeEnvironment);
+
   @override
-  void report(LocatedMessage message, List<LocatedMessage> context) {
-    compilerContext.options.report(message, Severity.error, context: context);
+  String freeTypeParameter(
+      List<TreeNode> context, TreeNode node, DartType type) {
+    final message =
+        codes.templateConstEvalFreeTypeParameter.withArguments(type);
+    return reportIt(context, message, node);
   }
 
   @override
-  void reportInvalidExpression(InvalidExpression node) {
-    // Assumed to be already reported.
+  String duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+    final message = codes.templateConstEvalDuplicateKey.withArguments(key);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidDartType(List<TreeNode> context, TreeNode node,
+      Constant receiver, DartType expectedType) {
+    final message = codes.templateConstEvalInvalidType.withArguments(
+        receiver, expectedType, receiver.getType(typeEnvironment));
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
+    final message = codes.templateConstEvalInvalidBinaryOperandType
+        .withArguments(op, receiver, expectedType, actualType);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+    final message = codes.templateConstEvalInvalidMethodInvocation
+        .withArguments(op, receiver);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Member target) {
+    final message = codes.templateConstEvalInvalidStaticInvocation
+        .withArguments(target.name.toString());
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    final message = codes.templateConstEvalInvalidStringInterpolationOperand
+        .withArguments(constant);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String invalidSymbolName(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    final message =
+        codes.templateConstEvalInvalidSymbolName.withArguments(constant);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
+    final message = codes.templateConstEvalZeroDivisor
+        .withArguments(op, '${receiver.value}');
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String negativeShift(List<TreeNode> context, TreeNode node,
+      IntConstant receiver, String op, IntConstant argument) {
+    final message = codes.templateConstEvalNegativeShift
+        .withArguments(op, '${receiver.value}', '${argument.value}');
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    final message =
+        codes.templateConstEvalNonConstantLiteral.withArguments(klass);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String failedAssertion(List<TreeNode> context, TreeNode node, String string) {
+    final message = string == null
+        ? codes.messageConstEvalFailedAssertion
+        : codes.templateConstEvalFailedAssertionWithMessage
+            .withArguments(string);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String nonConstantVariableGet(
+      List<TreeNode> context, TreeNode node, String variableName) {
+    final message = codes.templateConstEvalNonConstantVariableGet
+        .withArguments(variableName);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String deferredLibrary(
+      List<TreeNode> context, TreeNode node, String importName) {
+    final message =
+        codes.templateConstEvalDeferredLibrary.withArguments(importName);
+    return reportIt(context, message, node);
+  }
+
+  @override
+  String circularity(List<TreeNode> context, TreeNode node) {
+    final message = codes.messageConstEvalCircularity;
+    return reportIt(context, message, node);
+  }
+
+  String 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);
+    return locatedMessage.message;
   }
 }
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 5b299d9..9313820 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -13,11 +13,6 @@
 import 'package:build_integration/file_system/multi_root.dart'
     show MultiRootFileSystem, MultiRootFileSystemEntity;
 
-// TODO(askesc): We should not need to call the constant evaluator
-// explicitly once constant-update-2018 is shipped.
-import 'package:front_end/src/api_prototype/constant_evaluator.dart'
-    as constants;
-
 import 'package:front_end/src/api_unstable/vm.dart'
     show
         CompilerContext,
@@ -35,6 +30,7 @@
         parseExperimentalFlags,
         printDiagnosticMessage;
 
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 import 'package:kernel/ast.dart'
     show Component, Field, Library, Reference, StaticGet;
@@ -43,6 +39,7 @@
     show LimitedBinaryPrinter;
 import 'package:kernel/core_types.dart' show CoreTypes;
 import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
+import 'package:kernel/transformations/constants.dart' as constants;
 import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
 
 import 'bytecode/ast_remover.dart' show ASTRemover;
@@ -404,10 +401,13 @@
   final vmConstants = new vm_constants.VmConstantsBackend(coreTypes);
 
   await runWithFrontEndCompilerContext(source, compilerOptions, component, () {
+    final hierarchy = new ClassHierarchy(component);
+    final typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
+
     // TFA will remove constants fields which are unused (and respects the
     // vm/embedder entrypoints).
     constants.transformComponent(component, vmConstants, environmentDefines,
-        new ForwardConstantEvaluationErrors(),
+        new ForwardConstantEvaluationErrors(typeEnvironment),
         keepFields: true,
         evaluateAnnotations: true,
         enableAsserts: enableAsserts);
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index a04416a..5af1fdd 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -11,6 +11,7 @@
 import 'package:kernel/target/targets.dart';
 import 'package:kernel/transformations/mixin_full_resolution.dart'
     as transformMixins show transformLibraries;
+import 'package:kernel/transformations/constants.dart' show ConstantsBackend;
 import 'package:kernel/transformations/continuation.dart' as transformAsync
     show transformLibraries, transformProcedure;
 import 'package:kernel/vm/constants_native_effects.dart'