Error message for circular constant expressions

Handles the situations described in #34204 and #34189 when front-end
constant folding is enabled.

Change-Id: I64fd8449ac1661f6fe67411113c6f99d8009547c
Reviewed-on: https://dart-review.googlesource.com/c/90007
Reviewed-by: Kevin Millikin <kmillikin@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index e9c843d..8ca86a7 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -34,6 +34,7 @@
     show
         LocatedMessage,
         Message,
+        messageConstEvalCircularity,
         messageConstEvalContext,
         messageConstEvalFailedAssertion,
         noLength,
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 3c87fc7..43f48ce 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -974,6 +974,15 @@
         r"""Constant constructor can't call non-constant super constructors.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeConstEvalCircularity = messageConstEvalCircularity;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageConstEvalCircularity = const MessageCode(
+    "ConstEvalCircularity",
+    analyzerCodes: <String>["RECURSIVE_COMPILE_TIME_CONSTANT"],
+    message: r"""Constant expression depends on itself.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeConstEvalContext = messageConstEvalContext;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
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 0d0fc88..7927a7d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_constants.dart
@@ -30,6 +30,7 @@
     show
         Message,
         noLength,
+        messageConstEvalCircularity,
         messageConstEvalFailedAssertion,
         templateConstEvalDeferredLibrary,
         templateConstEvalDuplicateKey,
@@ -176,6 +177,11 @@
     return addProblem(
         node, templateConstEvalDeferredLibrary.withArguments(importName));
   }
+
+  @override
+  String circularity(List<TreeNode> context, TreeNode node) {
+    return addProblem(node, messageConstEvalCircularity);
+  }
 }
 
 class KernelConstantsBackend extends ConstantsBackend {
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 20e3338..445d797 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -56,6 +56,7 @@
 ConstConstructorNonFinalField/example: Fail
 ConstConstructorRedirectionToNonConst/analyzerCode: Fail # The analyzer doesn't report this error.
 ConstConstructorWithNonConstSuper/example: Fail
+ConstEvalCircularity/example: Fail
 ConstEvalContext/analyzerCode: Fail # This is just used for displaying the context.
 ConstEvalContext/example: Fail # This is just used for displaying the context.
 ConstEvalDeferredLibrary/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 50c5be3..7b717b1 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -146,6 +146,10 @@
 ConstEvalFreeTypeParameter:
   template: "The type '#type' is not a constant because it depends on a type parameter, only instantiated types are allowed."
 
+ConstEvalCircularity:
+  template: "Constant expression depends on itself."
+  analyzerCode: RECURSIVE_COMPILE_TIME_CONSTANT
+
 NotConstantExpression:
   template: "#string is not a constant expression."
   analyzerCode: NOT_CONSTANT_EXPRESSION
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index a76259f..4661791 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -415,7 +415,9 @@
       // environment.
       if (nodeCache.containsKey(node)) {
         final Constant constant = nodeCache[node];
-        if (constant == null) throw new _AbortCurrentEvaluation('circularity');
+        if (constant == null)
+          throw new _AbortCurrentEvaluation(
+              errorReporter.circularity(contextChain, node));
         return constant;
       }
 
@@ -1514,6 +1516,7 @@
       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 extends ErrorReporter {
@@ -1657,6 +1660,11 @@
         'expression',
         node);
   }
+
+  @override
+  String circularity(List<TreeNode> context, TreeNode node) {
+    return report(context, 'Constant expression depends on itself.', node);
+  }
 }
 
 class IsInstantiatedVisitor extends DartTypeVisitor<bool> {
diff --git a/pkg/vm/lib/constants_error_reporter.dart b/pkg/vm/lib/constants_error_reporter.dart
index 0842add..d2fdc95 100644
--- a/pkg/vm/lib/constants_error_reporter.dart
+++ b/pkg/vm/lib/constants_error_reporter.dart
@@ -140,6 +140,12 @@
     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);