[CFE] Use Fasta diagnostics in the constant evaluator.

The constant evaluator now generates all errors as Fasta diagnostic
messages. The ErrorReporter is simplified to just accept a diagnostic
message, or a notification that the constant evaluator encountered an
invalid expression (presumably put there due to an earlier error).

Also, the flow of control between the error reporter and the internal
abort exceptions is reversed, so the error reporter is now called as a
result of an abort exception being caught by the evaluate method.

Change-Id: I66f148cc4e202e328f895ae0b770f9b68c9f3c8e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96300
Reviewed-by: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/constants.dart b/pkg/dev_compiler/lib/src/kernel/constants.dart
index a59d0bd..7f550cb 100644
--- a/pkg/dev_compiler/lib/src/kernel/constants.dart
+++ b/pkg/dev_compiler/lib/src/kernel/constants.dart
@@ -170,5 +170,5 @@
 
   // Ignore reported errors.
   @override
-  report(context, message, node) => message;
+  reportMessage(Uri uri, int offset, String message) {}
 }
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index e3ef443..20ad6ad 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -34,25 +34,6 @@
 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/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index ac5cd3b..df58f84 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -32,6 +32,29 @@
 
 import 'package:kernel/target/targets.dart';
 
+import '../fasta_codes.dart'
+    show
+        LocatedMessage,
+        Message,
+        messageConstEvalCircularity,
+        messageConstEvalContext,
+        messageConstEvalFailedAssertion,
+        noLength,
+        templateConstEvalDeferredLibrary,
+        templateConstEvalDuplicateKey,
+        templateConstEvalFailedAssertionWithMessage,
+        templateConstEvalFreeTypeParameter,
+        templateConstEvalInvalidType,
+        templateConstEvalInvalidBinaryOperandType,
+        templateConstEvalInvalidMethodInvocation,
+        templateConstEvalInvalidStaticInvocation,
+        templateConstEvalInvalidStringInterpolationOperand,
+        templateConstEvalInvalidSymbolName,
+        templateConstEvalNegativeShift,
+        templateConstEvalNonConstantLiteral,
+        templateConstEvalNonConstantVariableGet,
+        templateConstEvalZeroDivisor;
+
 Component transformComponent(Component component, ConstantsBackend backend,
     Map<String, String> environmentDefines, ErrorReporter errorReporter,
     {bool keepFields: false,
@@ -411,6 +434,21 @@
         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,
@@ -419,8 +457,24 @@
     evaluationRoot = node;
     try {
       return _evaluateSubexpression(node);
-    } on _AbortCurrentEvaluation catch (e) {
-      return new UnevaluatedConstant(new InvalidExpression(e.message));
+    } 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);
     } finally {
       // Release collections used to keep track of unevaluated nodes.
       evaluationRoot = null;
@@ -429,6 +483,14 @@
     }
   }
 
+  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.
@@ -463,18 +525,15 @@
   }
 
   /// Evaluate [node] and possibly cache the evaluation result.
-  /// @throws _AbortCurrentEvaluation if expression can't be evaluated.
+  /// @throws _AbortDueToError or _AbortDueToInvalidExpression 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)) {
-        final Constant constant = nodeCache[node];
-        if (constant == null)
-          throw new _AbortCurrentEvaluation(
-              errorReporter.circularity(contextChain, node));
-        return constant;
+        return nodeCache[node] ?? report(node, messageConstEvalCircularity);
       }
 
       nodeCache[node] = null;
@@ -550,8 +609,8 @@
 
   visitListLiteral(ListLiteral node) {
     if (!node.isConst) {
-      throw new _AbortCurrentEvaluation(
-          errorReporter.nonConstLiteral(contextChain, node, 'List'));
+      return report(
+          node, templateConstEvalNonConstantLiteral.withArguments('List'));
     }
     final List<Constant> entries = new List<Constant>(node.expressions.length);
     for (int i = 0; i < node.expressions.length; ++i) {
@@ -574,8 +633,8 @@
 
   visitSetLiteral(SetLiteral node) {
     if (!node.isConst) {
-      throw new _AbortCurrentEvaluation(
-          errorReporter.nonConstLiteral(contextChain, node, 'Set'));
+      return report(
+          node, templateConstEvalNonConstantLiteral.withArguments('Set'));
     }
     final List<Constant> entries = new List<Constant>(node.expressions.length);
     for (int i = 0; i < node.expressions.length; ++i) {
@@ -598,8 +657,8 @@
 
   visitMapLiteral(MapLiteral node) {
     if (!node.isConst) {
-      throw new _AbortCurrentEvaluation(
-          errorReporter.nonConstLiteral(contextChain, node, 'Map'));
+      return report(
+          node, templateConstEvalNonConstantLiteral.withArguments('Map'));
     }
     final Set<Constant> usedKeys = new Set<Constant>();
     final List<ConstantMapEntry> entries =
@@ -611,8 +670,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.
-        throw new _AbortCurrentEvaluation(
-            errorReporter.duplicateKey(contextChain, node.entries[i], key));
+        return report(
+            node.entries[i], templateConstEvalDuplicateKey.withArguments(key));
       }
       entries[i] = new ConstantMapEntry(key, value);
     }
@@ -634,8 +693,8 @@
   }
 
   visitFunctionExpression(FunctionExpression node) {
-    throw new _AbortCurrentEvaluation(
-        errorReporter.nonConstLiteral(contextChain, node, 'Function'));
+    return report(
+        node, templateConstEvalNonConstantLiteral.withArguments('Function'));
   }
 
   visitConstructorInvocation(ConstructorInvocation node) {
@@ -699,8 +758,8 @@
               isValidSymbolName(nameValue.value)) {
             return canonicalize(new SymbolConstant(nameValue.value, null));
           }
-          throw new _AbortCurrentEvaluation(errorReporter.invalidSymbolName(
-              contextChain, node.arguments.positional.first, nameValue));
+          return report(node.arguments.positional.first,
+              templateConstEvalInvalidSymbolName.withArguments(nameValue));
         }
 
         return canonicalize(result);
@@ -900,30 +959,31 @@
               if (condition is BoolConstant) {
                 if (!condition.value) {
                   if (init.statement.message == null) {
-                    throw new _AbortCurrentEvaluation(
-                        errorReporter.failedAssertion(
-                            contextChain, init.statement.condition, null));
+                    return report(init.statement.condition,
+                        messageConstEvalFailedAssertion);
                   }
                   final Constant message =
                       _evaluateSubexpression(init.statement.message);
                   if (message is StringConstant) {
-                    throw new _AbortCurrentEvaluation(
-                        errorReporter.failedAssertion(contextChain,
-                            init.statement.condition, message.value));
+                    return report(
+                        init.statement.condition,
+                        templateConstEvalFailedAssertionWithMessage
+                            .withArguments(message.value));
                   }
-                  throw new _AbortCurrentEvaluation(
-                      errorReporter.invalidDartType(
-                          contextChain,
-                          init.statement.message,
+                  return report(
+                      init.statement.message,
+                      templateConstEvalInvalidType.withArguments(
                           message,
-                          typeEnvironment.stringType));
+                          typeEnvironment.stringType,
+                          message.getType(typeEnvironment)));
                 }
               } else {
-                throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
-                    contextChain,
+                return report(
                     init.statement.condition,
-                    condition,
-                    typeEnvironment.boolType));
+                    templateConstEvalInvalidType.withArguments(
+                        condition,
+                        typeEnvironment.boolType,
+                        condition.getType(typeEnvironment)));
               }
             }
           } else {
@@ -936,7 +996,7 @@
   }
 
   visitInvalidExpression(InvalidExpression node) {
-    throw new _AbortCurrentEvaluation(node.message);
+    return reportInvalidExpression(node);
   }
 
   visitMethodInvocation(MethodInvocation node) {
@@ -977,12 +1037,11 @@
               return canonicalize(
                   new StringConstant(receiver.value + other.value));
             }
-            throw new _AbortCurrentEvaluation(
-                errorReporter.invalidBinaryOperandType(
-                    contextChain,
-                    node,
-                    receiver,
+            return report(
+                node,
+                templateConstEvalInvalidBinaryOperandType.withArguments(
                     '+',
+                    receiver,
                     typeEnvironment.stringType,
                     other.getType(typeEnvironment)));
         }
@@ -1000,12 +1059,11 @@
         final op = node.name.name;
         if (other is IntConstant) {
           if ((op == '<<' || op == '>>') && other.value < 0) {
-            throw new _AbortCurrentEvaluation(errorReporter.negativeShift(
-                contextChain,
+            return report(
                 node.arguments.positional.first,
-                receiver,
-                op,
-                other));
+                // TODO(askesc): Change argument types in template to constants.
+                templateConstEvalNegativeShift.withArguments(
+                    op, '${receiver.value}', '${other.value}'));
           }
           switch (op) {
             case '|':
@@ -1028,8 +1086,11 @@
 
         if (other is IntConstant) {
           if (other.value == 0 && (op == '%' || op == '~/')) {
-            throw new _AbortCurrentEvaluation(errorReporter.zeroDivisor(
-                contextChain, node.arguments.positional.first, receiver, op));
+            return report(
+                node.arguments.positional.first,
+                // TODO(askesc): Change argument type in template to constant.
+                templateConstEvalZeroDivisor.withArguments(
+                    op, '${receiver.value}'));
           }
 
           return evaluateBinaryNumericOperation(
@@ -1038,12 +1099,11 @@
           return evaluateBinaryNumericOperation(
               node.name.name, receiver.value, other.value, node);
         }
-        throw new _AbortCurrentEvaluation(
-            errorReporter.invalidBinaryOperandType(
-                contextChain,
-                node,
+        return report(
+            node,
+            templateConstEvalInvalidBinaryOperandType.withArguments(
+                node.name.name,
                 receiver,
-                '${node.name.name}',
                 typeEnvironment.numType,
                 other.getType(typeEnvironment)));
       }
@@ -1063,18 +1123,19 @@
           return evaluateBinaryNumericOperation(
               node.name.name, receiver.value, value, node);
         }
-        throw new _AbortCurrentEvaluation(
-            errorReporter.invalidBinaryOperandType(
-                contextChain,
-                node,
+        return report(
+            node,
+            templateConstEvalInvalidBinaryOperandType.withArguments(
+                node.name.name,
                 receiver,
-                '${node.name.name}',
                 typeEnvironment.numType,
                 other.getType(typeEnvironment)));
       }
     }
-    throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
-        contextChain, node, receiver, node.name.name));
+    return report(
+        node,
+        templateConstEvalInvalidMethodInvocation.withArguments(
+            node.name.name, receiver));
   }
 
   visitLogicalExpression(LogicalExpression node) {
@@ -1095,17 +1156,18 @@
             return right;
           }
 
-          throw new _AbortCurrentEvaluation(
-              errorReporter.invalidBinaryOperandType(
-                  contextChain,
-                  node,
+          return report(
+              node,
+              templateConstEvalInvalidBinaryOperandType.withArguments(
+                  node.operator,
                   left,
-                  '${node.operator}',
                   typeEnvironment.boolType,
                   right.getType(typeEnvironment)));
         }
-        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
-            contextChain, node, left, '${node.operator}'));
+        return report(
+            node,
+            templateConstEvalInvalidMethodInvocation.withArguments(
+                node.operator, left));
       case '&&':
         if (left is BoolConstant) {
           if (!left.value) return falseConstant;
@@ -1115,24 +1177,27 @@
             return right;
           }
 
-          throw new _AbortCurrentEvaluation(
-              errorReporter.invalidBinaryOperandType(
-                  contextChain,
-                  node,
+          return report(
+              node,
+              templateConstEvalInvalidBinaryOperandType.withArguments(
+                  node.operator,
                   left,
-                  '${node.operator}',
                   typeEnvironment.boolType,
                   right.getType(typeEnvironment)));
         }
-        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
-            contextChain, node, left, '${node.operator}'));
+        return report(
+            node,
+            templateConstEvalInvalidMethodInvocation.withArguments(
+                node.operator, left));
       case '??':
         return (left is! NullConstant)
             ? left
             : _evaluateSubexpression(node.right);
       default:
-        throw new _AbortCurrentEvaluation(errorReporter.invalidMethodInvocation(
-            contextChain, node, left, '${node.operator}'));
+        return report(
+            node,
+            templateConstEvalInvalidMethodInvocation.withArguments(
+                node.operator, left));
     }
   }
 
@@ -1151,8 +1216,10 @@
               cloner.clone(node.otherwise),
               node.staticType));
     } else {
-      throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
-          contextChain, node, condition, typeEnvironment.boolType));
+      return report(
+          node,
+          templateConstEvalInvalidType.withArguments(condition,
+              typeEnvironment.boolType, condition.getType(typeEnvironment)));
     }
   }
 
@@ -1200,12 +1267,11 @@
     // variables might allow more than it should.
     final VariableDeclaration variable = node.variable;
     if (variable.parent is Let || _isFormalParameter(variable)) {
-      final Constant constant = env.lookupVariable(node.variable);
-      if (constant == null) {
-        throw new _AbortCurrentEvaluation(errorReporter.nonConstantVariableGet(
-            contextChain, node, variable.name));
-      }
-      return constant;
+      return env.lookupVariable(node.variable) ??
+          report(
+              node,
+              templateConstEvalNonConstantVariableGet
+                  .withArguments(variable.name));
     }
     if (variable.isConst) {
       return _evaluateSubexpression(variable.initializer);
@@ -1227,14 +1293,18 @@
             return _evaluateSubexpression(target.initializer);
           });
         }
-        throw new _AbortCurrentEvaluation(
-            errorReporter.invalidStaticInvocation(contextChain, node, target));
+        return report(
+            node,
+            templateConstEvalInvalidStaticInvocation
+                .withArguments(target.name.name));
       } else if (target is Procedure) {
         if (target.kind == ProcedureKind.Method) {
           return canonicalize(new TearOffConstant(target));
         }
-        throw new _AbortCurrentEvaluation(
-            errorReporter.invalidStaticInvocation(contextChain, node, target));
+        return report(
+            node,
+            templateConstEvalInvalidStaticInvocation
+                .withArguments(target.name.name));
       } else {
         throw new Exception(
             'No support for ${target.runtimeType} in a static-get.');
@@ -1257,8 +1327,10 @@
       } else if (constant is UnevaluatedConstant) {
         concatenated.add(constant);
       } else {
-        throw new _AbortCurrentEvaluation(errorReporter
-            .invalidStringInterpolationOperand(contextChain, node, constant));
+        return report(
+            node,
+            templateConstEvalInvalidStringInterpolationOperand
+                .withArguments(constant));
       }
     }
     if (concatenated.length > 1) {
@@ -1346,8 +1418,23 @@
         return identical(left, right) ? trueConstant : falseConstant;
       }
     }
-    throw new _AbortCurrentEvaluation(
-        errorReporter.invalidStaticInvocation(contextChain, node, 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.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));
   }
 
   visitAsExpression(AsExpression node) {
@@ -1368,8 +1455,10 @@
     if (constant is UnevaluatedConstant) {
       return unevaluated(node, new Not(unique(constant.expression)));
     }
-    throw new _AbortCurrentEvaluation(errorReporter.invalidDartType(
-        contextChain, node, constant, typeEnvironment.boolType));
+    return report(
+        node,
+        templateConstEvalInvalidType.withArguments(constant,
+            typeEnvironment.boolType, constant.getType(typeEnvironment)));
   }
 
   visitSymbolLiteral(SymbolLiteral node) {
@@ -1401,8 +1490,8 @@
 
   @override
   visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
-    throw new _AbortCurrentEvaluation(
-        errorReporter.deferredLibrary(contextChain, node, node.import.name));
+    return report(
+        node, templateConstEvalDeferredLibrary.withArguments(node.import.name));
   }
 
   // Helper methods:
@@ -1423,8 +1512,10 @@
     DartType constantType = constant.getType(typeEnvironment);
 
     if (!typeEnvironment.isSubtypeOf(constantType, type)) {
-      throw new _AbortCurrentEvaluation(
-          errorReporter.invalidDartType(contextChain, node, constant, type));
+      return report(
+          node,
+          templateConstEvalInvalidType.withArguments(
+              constant, type, constant.getType(typeEnvironment)));
     }
   }
 
@@ -1447,8 +1538,8 @@
     final result = env.subsituteType(type);
 
     if (!isInstantiated(result)) {
-      throw new _AbortCurrentEvaluation(
-          errorReporter.freeTypeParameter(contextChain, node, type));
+      return report(
+          node, templateConstEvalFreeTypeParameter.withArguments(type));
     }
 
     return result;
@@ -1637,201 +1728,51 @@
 }
 
 // Used as control-flow to abort the current evaluation.
-class _AbortCurrentEvaluation {
-  final String message;
-  _AbortCurrentEvaluation(this.message);
+class _AbortDueToError {
+  final TreeNode node;
+  final Message message;
+  final List<LocatedMessage> context;
+
+  _AbortDueToError(this.node, this.message, {this.context});
+}
+
+class _AbortDueToInvalidExpression {
+  final InvalidExpression node;
+
+  _AbortDueToInvalidExpression(this.node);
 }
 
 abstract class ErrorReporter {
   const ErrorReporter();
 
-  Uri getFileUri(TreeNode node) {
-    while (node is! FileUriNode) {
-      node = node.parent;
-    }
-    return (node as FileUriNode).fileUri;
-  }
+  void report(LocatedMessage message, List<LocatedMessage> context);
 
-  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);
+  void reportInvalidExpression(InvalidExpression node);
 }
 
-class SimpleErrorReporter extends ErrorReporter {
+class SimpleErrorReporter implements ErrorReporter {
   const SimpleErrorReporter();
 
-  String report(List<TreeNode> context, String what, TreeNode node) {
+  @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) {
     io.exitCode = 42;
-    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);
+    io.stderr.writeln('$uri:$offset Constant evaluation error: $message');
   }
 }
 
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 5b40187..5f8b4f5 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
@@ -4,31 +4,9 @@
 
 library fasta.kernel_constants;
 
-import 'package:kernel/ast.dart'
-    show Constant, DartType, IntConstant, Library, Member, Procedure, TreeNode;
+import 'package:kernel/ast.dart' show InvalidExpression, Library;
 
-import 'package:kernel/type_environment.dart' show TypeEnvironment;
-
-import '../fasta_codes.dart'
-    show
-        Message,
-        noLength,
-        messageConstEvalCircularity,
-        messageConstEvalFailedAssertion,
-        templateConstEvalDeferredLibrary,
-        templateConstEvalDuplicateKey,
-        templateConstEvalFailedAssertionWithMessage,
-        templateConstEvalFreeTypeParameter,
-        templateConstEvalInvalidBinaryOperandType,
-        templateConstEvalInvalidMethodInvocation,
-        templateConstEvalInvalidStaticInvocation,
-        templateConstEvalInvalidStringInterpolationOperand,
-        templateConstEvalInvalidSymbolName,
-        templateConstEvalInvalidType,
-        templateConstEvalNegativeShift,
-        templateConstEvalNonConstantLiteral,
-        templateConstEvalNonConstantVariableGet,
-        templateConstEvalZeroDivisor;
+import '../fasta_codes.dart' show LocatedMessage;
 
 import '../loader.dart' show Loader;
 
@@ -36,145 +14,18 @@
 
 class KernelConstantErrorReporter extends ErrorReporter {
   final Loader<Library> loader;
-  final TypeEnvironment typeEnvironment;
 
-  KernelConstantErrorReporter(this.loader, this.typeEnvironment);
+  KernelConstantErrorReporter(this.loader);
 
-  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 report(LocatedMessage message, List<LocatedMessage> context) {
+    loader.addProblem(
+        message.messageObject, message.charOffset, message.length, message.uri,
+        context: context);
   }
 
   @override
-  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);
+  void reportInvalidExpression(InvalidExpression node) {
+    // Assumed to be already reported.
   }
 }
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 a797830..5473197 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -758,7 +758,7 @@
           backendTarget.constantsBackend(loader.coreTypes),
           environmentDefines,
           environment,
-          new KernelConstantErrorReporter(loader, environment),
+          new KernelConstantErrorReporter(loader),
           enableAsserts: enableAsserts);
       ticker.logMs("Evaluated constants");
     }
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 0dbc532..3e69e5a 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -58,7 +58,7 @@
       onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
   final typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
   final constantsBackend = new VmConstantsBackend(coreTypes);
-  final errorReporter = new ForwardConstantEvaluationErrors(typeEnvironment);
+  final errorReporter = new ForwardConstantEvaluationErrors();
   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 88eeeb6..724f5d8 100644
--- a/pkg/vm/lib/constants_error_reporter.dart
+++ b/pkg/vm/lib/constants_error_reporter.dart
@@ -10,162 +10,22 @@
     as constants;
 
 import 'package:front_end/src/api_unstable/vm.dart'
-    show CompilerContext, Severity;
+    show CompilerContext, LocatedMessage, Severity;
 
-import 'package:front_end/src/api_unstable/vm.dart' as codes;
+import 'package:kernel/ast.dart' show InvalidExpression;
 
-import 'package:kernel/ast.dart'
-    show Constant, DartType, IntConstant, Member, TreeNode;
-import 'package:kernel/type_environment.dart' show TypeEnvironment;
-
-class ForwardConstantEvaluationErrors extends constants.ErrorReporter {
+class ForwardConstantEvaluationErrors implements 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
-  String freeTypeParameter(
-      List<TreeNode> context, TreeNode node, DartType type) {
-    final message =
-        codes.templateConstEvalFreeTypeParameter.withArguments(type);
-    return reportIt(context, message, node);
+  void report(LocatedMessage message, List<LocatedMessage> context) {
+    compilerContext.options.report(message, Severity.error, context: context);
   }
 
   @override
-  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;
+  void reportInvalidExpression(InvalidExpression node) {
+    // Assumed to be already reported.
   }
 }
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 049e7bb..5b299d9 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -35,7 +35,6 @@
         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;
@@ -405,13 +404,10 @@
   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(typeEnvironment),
+        new ForwardConstantEvaluationErrors(),
         keepFields: true,
         evaluateAnnotations: true,
         enableAsserts: enableAsserts);