[Kernel] Add syntax for unevaluated constants

Add Kernel syntactic support for constants constructed by
bool.fromEnvironment, int.fromEnvironment, and String.fromEnvironment.
These values are not necessarily available at compile time.  Because
constants can depend on these values, there is also an unevaluated constant
that represents an expression depending on environment values.

This syntax is not yet produced by the Fasta compiler.

Change-Id: Ie96ea7f60a7efcd35ac802b320a00f398d41232e
Reviewed-on: https://dart-review.googlesource.com/c/88827
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index b90b937..035611f 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -5119,6 +5119,14 @@
   visitTypeLiteralConstant(node) => defaultConstant(node);
   @override
   visitPartialInstantiationConstant(node) => defaultConstant(node);
+  @override
+  visitEnvironmentBoolConstant(node) => defaultConstant(node);
+  @override
+  visitEnvironmentIntConstant(node) => defaultConstant(node);
+  @override
+  visitEnvironmentStringConstant(node) => defaultConstant(node);
+  @override
+  visitUnevaluatedConstant(node) => defaultConstant(node);
 }
 
 bool isSdkInternalRuntime(Library l) =>
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
index 049af7e..38fa92c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
@@ -14,6 +14,9 @@
         DartType,
         DoubleConstant,
         DynamicType,
+        EnvironmentBoolConstant,
+        EnvironmentIntConstant,
+        EnvironmentStringConstant,
         Field,
         FunctionType,
         InvalidType,
@@ -32,6 +35,7 @@
         TypeLiteralConstant,
         TypeParameter,
         TypeParameterType,
+        UnevaluatedConstant,
         VoidType;
 
 import 'package:kernel/visitor.dart' show ConstantVisitor, DartTypeVisitor;
@@ -41,6 +45,8 @@
 import '../fasta_codes.dart'
     show Message, templateTypeOrigin, templateTypeOriginWithFileUri;
 
+import '../problems.dart' show unsupported;
+
 /// A pretty-printer for Kernel types and constants with the ability to label
 /// raw types with numeric markers in Dart comments (e.g. `/*1*/`) to
 /// distinguish different types with the same name. This is used in diagnostic
@@ -306,6 +312,22 @@
   void visitTypeLiteralConstant(TypeLiteralConstant node) {
     node.type.accept(this);
   }
+
+  void visitEnvironmentBoolConstant(EnvironmentBoolConstant node) {
+    unsupported('printing unevaluated constants', -1, null);
+  }
+
+  void visitEnvironmentIntConstant(EnvironmentIntConstant node) {
+    unsupported('printing unevaluated constants', -1, null);
+  }
+
+  void visitEnvironmentStringConstant(EnvironmentStringConstant node) {
+    unsupported('printing unevaluated constants', -1, null);
+  }
+
+  void visitUnevaluatedConstant(UnevaluatedConstant node) {
+    unsupported('printing unevaluated constants', -1, null);
+  }
 }
 
 class LabeledClassName {
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index b1d825a..c70d840 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -60,7 +60,7 @@
 closure: TextSerializationFailure # Was: Pass
 co19_language_metadata_syntax_t04: TextSerializationFailure # Was: Pass
 constructor_const_inference: TextSerializationFailure # Was: Pass
-constructor_cycle: Crash # Was: Pass
+constructor_cycle: TextSerializationFailure # Was: Pass
 constructor_function_types: TextSerializationFailure # Was: Pass
 constructor_initializer_invalid: TextSerializationFailure # Was: RuntimeError # Fails execution after recovery
 continue_inference_after_error_lib: TextSerializationFailure # Was: Pass
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 865beb6..9408027 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -131,7 +131,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 15;
+  UInt32 formatVersion = 16;
   Library[] libraries;
   UriSource sourceMap;
   List<CanonicalName> canonicalNames;
@@ -926,6 +926,29 @@
   DartType type;
 }
 
+type EnvironmentBoolConstant extends Constant {
+  Byte tag = 12;
+  StringReference name;
+  ConstantReference defaultValue;
+}
+
+type EnvironmentIntConstant extends Constant {
+  Byte tag = 13;
+  StringReference name;
+  ConstantReference defaultValue;
+}
+
+type EnvironmentStringConstant extends Constant {
+  Byte tag = 14;
+  StringReference name;
+  ConstantReference defaultValue;
+}
+
+type UnevaluatedConstant extends Constant {
+  Byte tag = 15;
+  Expression expression;
+}
+
 abstract type Statement extends Node {}
 
 type ExpressionStatement extends Statement {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 0cf92d1..ce6319f 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -83,7 +83,7 @@
   const Node();
 
   accept(Visitor v);
-  visitChildren(Visitor v);
+  void visitChildren(Visitor v);
 
   /// Returns the textual representation of this node for use in debugging.
   ///
@@ -5440,6 +5440,67 @@
   DartType getType(TypeEnvironment types) => types.typeType;
 }
 
+abstract class EnvironmentConstant extends Constant {
+  final String name;
+  final Constant defaultValue;
+
+  EnvironmentConstant(this.name, this.defaultValue);
+  visitChildren(Visitor v) {
+    defaultValue.acceptReference(v);
+  }
+}
+
+class EnvironmentBoolConstant extends EnvironmentConstant {
+  EnvironmentBoolConstant(String name, Constant defaultValue)
+      : super(name, defaultValue);
+
+  accept(ConstantVisitor v) => v.visitEnvironmentBoolConstant(this);
+  acceptReference(Visitor v) {
+    return v.visitEnvironmentBoolConstantReference(this);
+  }
+
+  DartType getType(TypeEnvironment types) => types.boolType;
+}
+
+class EnvironmentIntConstant extends EnvironmentConstant {
+  EnvironmentIntConstant(String name, Constant defaultValue)
+      : super(name, defaultValue);
+
+  accept(ConstantVisitor v) => v.visitEnvironmentIntConstant(this);
+  acceptReference(Visitor v) {
+    return v.visitEnvironmentIntConstantReference(this);
+  }
+
+  DartType getType(TypeEnvironment types) => types.boolType;
+}
+
+class EnvironmentStringConstant extends EnvironmentConstant {
+  EnvironmentStringConstant(String name, Constant defaultValue)
+      : super(name, defaultValue);
+
+  accept(ConstantVisitor v) => v.visitEnvironmentStringConstant(this);
+  acceptReference(Visitor v) {
+    return v.visitEnvironmentStringConstantReference(this);
+  }
+
+  DartType getType(TypeEnvironment types) => types.boolType;
+}
+
+class UnevaluatedConstant extends Constant {
+  final Expression expression;
+
+  UnevaluatedConstant(this.expression);
+
+  visitChildren(Visitor v) {
+    expression.accept(v);
+  }
+
+  accept(ConstantVisitor v) => v.visitUnevaluatedConstant(this);
+  acceptReference(Visitor v) => v.visitUnevaluatedConstantReference(this);
+
+  DartType getType(TypeEnvironment types) => expression.getStaticType(types);
+}
+
 // ------------------------------------------------------------------------
 //                                COMPONENT
 // ------------------------------------------------------------------------
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 0c9b61b..59148f3 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -270,6 +270,21 @@
       case ConstantTag.TypeLiteralConstant:
         final DartType type = readDartType();
         return new TypeLiteralConstant(type);
+      case ConstantTag.EnvironmentBoolConstant:
+        final String name = readStringReference();
+        final Constant defaultValue = readConstantReference();
+        return new EnvironmentBoolConstant(name, defaultValue);
+      case ConstantTag.EnvironmentIntConstant:
+        final String name = readStringReference();
+        final Constant defaultValue = readConstantReference();
+        return new EnvironmentIntConstant(name, defaultValue);
+      case ConstantTag.EnvironmentStringConstant:
+        final String name = readStringReference();
+        final Constant defaultValue = readConstantReference();
+        return new EnvironmentStringConstant(name, defaultValue);
+      case ConstantTag.UnevaluatedConstant:
+        final Expression expression = readExpression();
+        return new UnevaluatedConstant(expression);
     }
 
     throw fail('unexpected constant tag: $constantTag');
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 892a72b..2e5bb5d 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -243,6 +243,21 @@
     } else if (constant is TypeLiteralConstant) {
       writeByte(ConstantTag.TypeLiteralConstant);
       writeDartType(constant.type);
+    } else if (constant is EnvironmentBoolConstant) {
+      writeByte(ConstantTag.EnvironmentBoolConstant);
+      writeStringReference(constant.name);
+      writeConstantReference(constant.defaultValue);
+    } else if (constant is EnvironmentIntConstant) {
+      writeByte(ConstantTag.EnvironmentIntConstant);
+      writeStringReference(constant.name);
+      writeConstantReference(constant.defaultValue);
+    } else if (constant is EnvironmentStringConstant) {
+      writeByte(ConstantTag.EnvironmentStringConstant);
+      writeStringReference(constant.name);
+      writeConstantReference(constant.defaultValue);
+    } else if (constant is UnevaluatedConstant) {
+      writeByte(ConstantTag.UnevaluatedConstant);
+      writeNode(constant.expression);
     } else {
       throw new ArgumentError('Unsupported constant $constant');
     }
@@ -2048,6 +2063,39 @@
   }
 
   @override
+  void visitEnvironmentBoolConstant(EnvironmentBoolConstant node) {
+    throw new UnsupportedError('serialization of EnvironmentBoolConstants');
+  }
+
+  @override
+  void visitEnvironmentBoolConstantReference(EnvironmentBoolConstant node) {
+    throw new UnsupportedError(
+        'serialization of EnvironmentBoolConstant references');
+  }
+
+  @override
+  void visitEnvironmentIntConstant(EnvironmentIntConstant node) {
+    throw new UnsupportedError('serialization of EnvironmentIntConstants');
+  }
+
+  @override
+  void visitEnvironmentIntConstantReference(EnvironmentIntConstant node) {
+    throw new UnsupportedError(
+        'serialization of EnvironmentIntConstant references');
+  }
+
+  @override
+  void visitEnvironmentStringConstant(EnvironmentStringConstant node) {
+    throw new UnsupportedError('serialization of EnvironmentStringConstants');
+  }
+
+  @override
+  void visitEnvironmentStringConstantReference(EnvironmentStringConstant node) {
+    throw new UnsupportedError(
+        'serialization of EnvironmentStringConstant references');
+  }
+
+  @override
   void visitFieldReference(Field node) {
     throw new UnsupportedError('serialization of Field references');
   }
@@ -2192,6 +2240,17 @@
   void visitTypedefReference(Typedef node) {
     throw new UnsupportedError('serialization of Typedef references');
   }
+
+  @override
+  void visitUnevaluatedConstant(UnevaluatedConstant node) {
+    throw new UnsupportedError('serialization of UnevaluatedConstants');
+  }
+
+  @override
+  void visitUnevaluatedConstantReference(UnevaluatedConstant node) {
+    throw new UnsupportedError(
+        'serialization of UnevaluatedConstant references');
+  }
 }
 
 typedef bool LibraryFilter(Library _);
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index a53ccc7..6a34a73 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -117,9 +117,10 @@
   static const int MemberReference = 101;
 
   static const int ConstantExpression = 107;
-  // 108 is occupied by [RedirectingFactoryConstructor] (member).
-  // 109 is occupied by [SetLiteral] (expression).
-  // 110 is occupied by [ConstSetLiteral] (expression).
+
+  /// 108 is occupied by [RedirectingFactoryConstructor] (member).
+  /// 109 is occupied by [SetLiteral] (expression).
+  /// 110 is occupied by [ConstSetLiteral] (expression).
 
   static const int SpecializedTagHighBit = 0x80; // 10000000
   static const int SpecializedTagMask = 0xF8; // 11111000
@@ -136,7 +137,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 15;
+  static const int BinaryFormatVersion = 16;
 }
 
 abstract class ConstantTag {
@@ -152,4 +153,8 @@
   static const int PartialInstantiationConstant = 9;
   static const int TearOffConstant = 10;
   static const int TypeLiteralConstant = 11;
+  static const int EnvironmentBoolConstant = 12;
+  static const int EnvironmentIntConstant = 13;
+  static const int EnvironmentStringConstant = 14;
+  static const int UnevaluatedConstant = 15;
 }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 57eb05c..2f74c96 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1911,6 +1911,24 @@
     endLine(sb.toString());
   }
 
+  visitEnvironmentBoolConstant(EnvironmentBoolConstant node) {
+    final String name = syntheticNames.nameConstant(node);
+    final String defaultValue = syntheticNames.nameConstant(node.defaultValue);
+    endLine('  $name = bool.fromEnvironment(${node.name}, ${defaultValue})');
+  }
+
+  visitEnvironmentIntConstant(EnvironmentIntConstant node) {
+    final String name = syntheticNames.nameConstant(node);
+    final String defaultValue = syntheticNames.nameConstant(node.defaultValue);
+    endLine('  $name = int.fromEnvironment(${node.name}, ${defaultValue})');
+  }
+
+  visitEnvironmentStringConstant(EnvironmentStringConstant node) {
+    final String name = syntheticNames.nameConstant(node);
+    final String defaultValue = syntheticNames.nameConstant(node.defaultValue);
+    endLine('  $name = String.fromEnvironment(${node.name}, ${defaultValue})');
+  }
+
   defaultNode(Node node) {
     write('<${node.runtimeType}>');
   }
diff --git a/pkg/kernel/lib/text/text_serialization_verifier.dart b/pkg/kernel/lib/text/text_serialization_verifier.dart
index d74689e..644f15a 100644
--- a/pkg/kernel/lib/text/text_serialization_verifier.dart
+++ b/pkg/kernel/lib/text/text_serialization_verifier.dart
@@ -217,28 +217,16 @@
 
   @override
   void visitRedirectingFactoryConstructorReference(
-      RedirectingFactoryConstructor node) {
-    storeLastSeenUriAndOffset(node);
-    node.visitChildren(this);
-  }
+      RedirectingFactoryConstructor node) {}
 
   @override
-  void visitProcedureReference(Procedure node) {
-    storeLastSeenUriAndOffset(node);
-    node.visitChildren(this);
-  }
+  void visitProcedureReference(Procedure node) {}
 
   @override
-  void visitConstructorReference(Constructor node) {
-    storeLastSeenUriAndOffset(node);
-    node.visitChildren(this);
-  }
+  void visitConstructorReference(Constructor node) {}
 
   @override
-  void visitFieldReference(Field node) {
-    storeLastSeenUriAndOffset(node);
-    node.visitChildren(this);
-  }
+  void visitFieldReference(Field node) {}
 
   @override
   void visitTypeLiteralConstantReference(TypeLiteralConstant node) {
@@ -314,18 +302,36 @@
   }
 
   @override
-  void visitTypedefReference(Typedef node) {
+  void visitEnvironmentBoolConstantReference(EnvironmentBoolConstant node) {
     storeLastSeenUriAndOffset(node);
     node.visitChildren(this);
   }
 
   @override
-  void visitClassReference(Class node) {
+  void visitEnvironmentIntConstantReference(EnvironmentIntConstant node) {
     storeLastSeenUriAndOffset(node);
     node.visitChildren(this);
   }
 
   @override
+  void visitEnvironmentStringConstantReference(EnvironmentStringConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitUnevaluatedConstantReference(UnevaluatedConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitTypedefReference(Typedef node) {}
+
+  @override
+  void visitClassReference(Class node) {}
+
+  @override
   void visitTypeLiteralConstant(TypeLiteralConstant node) {
     storeLastSeenUriAndOffset(node);
     node.visitChildren(this);
@@ -398,6 +404,30 @@
   }
 
   @override
+  void visitEnvironmentBoolConstant(EnvironmentBoolConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitEnvironmentIntConstant(EnvironmentIntConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitEnvironmentStringConstant(EnvironmentStringConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitUnevaluatedConstant(UnevaluatedConstant node) {
+    storeLastSeenUriAndOffset(node);
+    node.visitChildren(this);
+  }
+
+  @override
   void visitTypedefType(TypedefType node) {
     storeLastSeenUriAndOffset(node);
     makeDartTypeRoundTrip(node);
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index 177a883..79a3be8 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -295,6 +295,13 @@
       defaultConstant(node);
   R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
   R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
+  R visitEnvironmentBoolConstant(EnvironmentBoolConstant node) =>
+      defaultConstant(node);
+  R visitEnvironmentIntConstant(EnvironmentIntConstant node) =>
+      defaultConstant(node);
+  R visitEnvironmentStringConstant(EnvironmentStringConstant node) =>
+      defaultConstant(node);
+  R visitUnevaluatedConstant(UnevaluatedConstant node) => defaultConstant(node);
 }
 
 class MemberReferenceVisitor<R> {
@@ -348,6 +355,13 @@
       defaultConstant(node);
   R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
   R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
+  R visitEnvironmentBoolConstant(EnvironmentBoolConstant node) =>
+      defaultConstant(node);
+  R visitEnvironmentIntConstant(EnvironmentIntConstant node) =>
+      defaultConstant(node);
+  R visitEnvironmentStringConstant(EnvironmentStringConstant node) =>
+      defaultConstant(node);
+  R visitUnevaluatedConstant(UnevaluatedConstant node) => defaultConstant(node);
 
   // Class references
   R visitClassReference(Class node) => null;
@@ -380,6 +394,14 @@
       defaultConstantReference(node);
   R visitTypeLiteralConstantReference(TypeLiteralConstant node) =>
       defaultConstantReference(node);
+  R visitEnvironmentBoolConstantReference(EnvironmentBoolConstant node) =>
+      defaultConstantReference(node);
+  R visitEnvironmentIntConstantReference(EnvironmentIntConstant node) =>
+      defaultConstantReference(node);
+  R visitEnvironmentStringConstantReference(EnvironmentStringConstant node) =>
+      defaultConstantReference(node);
+  R visitUnevaluatedConstantReference(UnevaluatedConstant node) =>
+      defaultConstantReference(node);
 
   // Member references
   R defaultMemberReference(Member node) => null;
diff --git a/runtime/vm/compiler/frontend/constant_evaluator.cc b/runtime/vm/compiler/frontend/constant_evaluator.cc
index 8d1c6da..3ac3ecc 100644
--- a/runtime/vm/compiler/frontend/constant_evaluator.cc
+++ b/runtime/vm/compiler/frontend/constant_evaluator.cc
@@ -1293,6 +1293,14 @@
         // Note: This is already lowered to InstanceConstant/ListConstant.
         UNREACHABLE();
         break;
+      case kEnvironmentBoolConstant:
+      case kEnvironmentIntConstant:
+      case kEnvironmentStringConstant:
+      case kUnevaluatedConstant:
+        // We should not see unevaluated constants in the constant table, they
+        // should have been fully evaluated before we get them.
+        UNREACHABLE();
+        break;
       default:
         UNREACHABLE();
     }
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index ddc9884..4710aad 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -17,7 +17,7 @@
 // package:kernel/binary.md.
 
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
-static const uint32_t kBinaryFormatVersion = 15;
+static const uint32_t kBinaryFormatVersion = 16;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
@@ -147,6 +147,12 @@
   kPartialInstantiationConstant = 9,
   kTearOffConstant = 10,
   kTypeLiteralConstant = 11,
+  // These constants are not expected to be seen by the VM, because all
+  // constants are fully evaluated.
+  kEnvironmentBoolConstant = 12,
+  kEnvironmentIntConstant = 13,
+  kEnvironmentStringConstant = 14,
+  kUnevaluatedConstant = 15,
 };
 
 static const int SpecializedIntLiteralBias = 3;