[Kernel] Add BlockExpression to the Kernel language

This is not yet used or tested.

Change-Id: Id050802926ad6452df3c39ace12ea5dd56d4faaa
Reviewed-on: https://dart-review.googlesource.com/c/87970
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index a56cbc2..7df0400 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -5028,6 +5028,11 @@
   }
 
   @override
+  visitBlockExpression(BlockExpression node) {
+    throw UnimplementedError('ProgramCompiler.visitBlockExpression');
+  }
+
+  @override
   visitInstantiation(Instantiation node) {
     return runtimeCall('gbind(#, #)', [
       _visitExpression(node.expression),
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 8ea99dc..5071104 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -137,7 +137,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 18;
+  UInt32 formatVersion = 19;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
   UriSource sourceMap;
@@ -845,6 +845,12 @@
   Expression body;
 }
 
+type BlockExpression extends Expression {
+  Byte tag = 82;
+  List<Statement> body;
+  Expression value;
+}
+
 type Instantiation extends Expression {
   Byte tag = 54;
   Expression expression;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 2da3cd5..a5132fc 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3644,6 +3644,39 @@
   }
 }
 
+class BlockExpression extends Expression {
+  final List<Statement> statements;
+  Expression value;
+
+  BlockExpression(this.statements, this.value) {
+    // Ensure statements is mutable.
+    assert((statements
+          ..add(null)
+          ..removeLast()) !=
+        null);
+    setParents(statements, this);
+    value?.parent = this;
+  }
+
+  DartType getStaticType(TypeEnvironment types) => value.getStaticType(types);
+
+  accept(ExpressionVisitor v) => v.visitBlockExpression(this);
+  accept1(ExpressionVisitor1 v, arg) => v.visitBlockExpression(this, arg);
+
+  visitChildren(Visitor v) {
+    visitList(statements, v);
+    value?.accept(v);
+  }
+
+  transformChildren(Transformer v) {
+    transformList(statements, v, this);
+    if (value != null) {
+      value = value.accept(v);
+      value?.parent = this;
+    }
+  }
+}
+
 /// Attempt to load the library referred to by a deferred import.
 ///
 /// This instruction is concerned with:
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index cc11d87..98c5d46 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1593,6 +1593,12 @@
         var body = readExpression();
         variableStack.length = stackHeight;
         return new Let(variable, body);
+      case Tag.BlockExpression:
+        int stackHeight = variableStack.length;
+        var statements = readStatementList();
+        var value = readExpression();
+        variableStack.length = stackHeight;
+        return new BlockExpression(statements, value);
       case Tag.Instantiation:
         var expression = readExpression();
         var typeArguments = readDartTypeList();
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 1b945ba..bce361f 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1614,6 +1614,16 @@
   }
 
   @override
+  void visitBlockExpression(BlockExpression node) {
+    writeByte(Tag.BlockExpression);
+    _variableIndexer ??= new VariableIndexer();
+    _variableIndexer.pushScope();
+    writeNodeList(node.statements);
+    writeNode(node.value);
+    _variableIndexer.popScope();
+  }
+
+  @override
   void visitInstantiation(Instantiation node) {
     writeByte(Tag.Instantiation);
     writeNode(node.expression);
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 280454f..b4bf895 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -67,6 +67,7 @@
   static const int AwaitExpression = 51;
   static const int FunctionExpression = 52;
   static const int Let = 53;
+  static const int BlockExpression = 82;
   static const int Instantiation = 54;
   static const int PositiveIntLiteral = 55;
   static const int NegativeIntLiteral = 56;
@@ -98,6 +99,7 @@
   static const int FunctionDeclaration = 79;
   static const int AsyncForInStatement = 80;
   static const int AssertBlock = 81;
+  // 82 is occupied by [BlockExpression] (expression).
 
   // Types
   static const int TypedefType = 87;
@@ -137,7 +139,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 = 18;
+  static const int BinaryFormatVersion = 19;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 17ade68..05fd20f 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -277,6 +277,11 @@
     return new Let(newVariable, clone(node.body));
   }
 
+  visitBlockExpression(BlockExpression node) {
+    return new BlockExpression(
+        node.statements.map(clone).toList(), clone(node.value));
+  }
+
   visitExpressionStatement(ExpressionStatement node) {
     return new ExpressionStatement(clone(node.expression));
   }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index dc0a65e..c110f74 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1392,6 +1392,21 @@
     writeExpression(node.body);
   }
 
+  visitBlockExpression(BlockExpression node) {
+    writeSpaced('block');
+    if (node.statements.isEmpty) {
+      writeSymbol('{');
+    } else {
+      endLine('{');
+    }
+    ++indentation;
+    node.statements.forEach(writeNode);
+    --indentation;
+    writeIndentation();
+    writeSymbol('} =>');
+    writeExpression(node.value);
+  }
+
   visitInstantiation(Instantiation node) {
     writeExpression(node.expression);
     writeSymbol('<');
diff --git a/pkg/kernel/lib/text/text_serialization_verifier.dart b/pkg/kernel/lib/text/text_serialization_verifier.dart
index e666451..02ce55f 100644
--- a/pkg/kernel/lib/text/text_serialization_verifier.dart
+++ b/pkg/kernel/lib/text/text_serialization_verifier.dart
@@ -756,6 +756,12 @@
   }
 
   @override
+  void visitBlockExpression(BlockExpression node) {
+    storeLastSeenUriAndOffset(node);
+    makeExpressionRoundTrip(node);
+  }
+
+  @override
   void visitNullLiteral(NullLiteral node) {
     storeLastSeenUriAndOffset(node);
     makeExpressionRoundTrip(node);
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 11503d0..d41de601 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -489,6 +489,12 @@
   }
 
   @override
+  DartType visitBlockExpression(BlockExpression node) {
+    node.statements.forEach(visitStatement);
+    return visitExpression(node.value);
+  }
+
+  @override
   DartType visitInstantiation(Instantiation node) {
     DartType type = visitExpression(node.expression);
     if (type is! FunctionType) {
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index 85a8177..fc09f57 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -338,6 +338,10 @@
     visitWithLocalScope(node);
   }
 
+  visitBlockExpression(BlockExpression node) {
+    visitWithLocalScope(node);
+  }
+
   visitCatch(Catch node) {
     bool savedInCatchBlock = inCatchBlock;
     inCatchBlock = true;
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index a83374b..4d2b2bb 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -57,6 +57,7 @@
   R visitBoolLiteral(BoolLiteral node) => defaultBasicLiteral(node);
   R visitNullLiteral(NullLiteral node) => defaultBasicLiteral(node);
   R visitLet(Let node) => defaultExpression(node);
+  R visitBlockExpression(BlockExpression node) => defaultExpression(node);
   R visitInstantiation(Instantiation node) => defaultExpression(node);
   R visitLoadLibrary(LoadLibrary node) => defaultExpression(node);
   R visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
@@ -179,6 +180,7 @@
   R visitBoolLiteral(BoolLiteral node) => defaultBasicLiteral(node);
   R visitNullLiteral(NullLiteral node) => defaultBasicLiteral(node);
   R visitLet(Let node) => defaultExpression(node);
+  R visitBlockExpression(BlockExpression node) => defaultExpression(node);
   R visitInstantiation(Instantiation node) => defaultExpression(node);
   R visitLoadLibrary(LoadLibrary node) => defaultExpression(node);
   R visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
@@ -516,6 +518,8 @@
   R visitBoolLiteral(BoolLiteral node, T arg) => defaultBasicLiteral(node, arg);
   R visitNullLiteral(NullLiteral node, T arg) => defaultBasicLiteral(node, arg);
   R visitLet(Let node, T arg) => defaultExpression(node, arg);
+  R visitBlockExpression(BlockExpression node, T arg) =>
+      defaultExpression(node, arg);
   R visitInstantiation(Instantiation node, T arg) =>
       defaultExpression(node, arg);
   R visitLoadLibrary(LoadLibrary node, T arg) => defaultExpression(node, arg);
diff --git a/runtime/vm/compiler/frontend/constant_evaluator.cc b/runtime/vm/compiler/frontend/constant_evaluator.cc
index 9c9b8ec..a16e229 100644
--- a/runtime/vm/compiler/frontend/constant_evaluator.cc
+++ b/runtime/vm/compiler/frontend/constant_evaluator.cc
@@ -117,6 +117,10 @@
       case kLet:
         EvaluateLet();
         break;
+      case kBlockExpression: {
+        UNIMPLEMENTED();
+        break;
+      }
       case kInstantiation:
         EvaluatePartialTearoffInstantiation();
         break;
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 10659cb..948a0d7 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -2126,6 +2126,10 @@
       return BuildFunctionExpression();
     case kLet:
       return BuildLet(position);
+    case kBlockExpression: {
+      UNIMPLEMENTED();
+      break;
+    }
     case kBigIntLiteral:
       return BuildBigIntLiteral(position);
     case kStringLiteral:
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index 03a4e59..58675d6 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -505,6 +505,10 @@
       CalculateVariableDeclarationFingerprint();  // read variable declaration.
       CalculateExpressionFingerprint();           // read expression.
       return;
+    case kBlockExpression: {
+      UNIMPLEMENTED();
+      return;
+    }
     case kInstantiation:
       CalculateExpressionFingerprint();       // read expression.
       CalculateListOfDartTypesFingerprint();  // read type arguments.
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 62f2c00..90ff779 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2255,6 +2255,10 @@
       SkipVariableDeclaration();  // read variable declaration.
       SkipExpression();           // read expression.
       return;
+    case kBlockExpression: {
+      UNIMPLEMENTED();
+      return;
+    }
     case kInstantiation:
       SkipExpression();       // read expression.
       SkipListOfDartTypes();  // read type arguments.
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index d585002..71c2e61 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -832,6 +832,10 @@
       ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position());
       return;
     }
+    case kBlockExpression: {
+      UNIMPLEMENTED();
+      return;
+    }
     case kBigIntLiteral:
       helper_.SkipStringReference();  // read string reference.
       return;
diff --git a/runtime/vm/kernel_binary.cc b/runtime/vm/kernel_binary.cc
index 2dd239e..0824578 100644
--- a/runtime/vm/kernel_binary.cc
+++ b/runtime/vm/kernel_binary.cc
@@ -68,7 +68,8 @@
   }
 
   uint32_t formatVersion = reader->ReadUInt32();
-  if (formatVersion != kBinaryFormatVersion) {
+  if ((formatVersion < kMinSupportedKernelFormatVersion) ||
+      (formatVersion > kMaxSupportedKernelFormatVersion)) {
     if (error != nullptr) {
       *error = kKernelInvalidBinaryFormatVersion;
     }
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 85ed239..0cc7f0e 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -17,7 +17,10 @@
 // package:kernel/binary.md.
 
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
-static const uint32_t kBinaryFormatVersion = 18;
+
+// Both version numbers are inclusive.
+static const uint32_t kMinSupportedKernelFormatVersion = 18;
+static const uint32_t kMaxSupportedKernelFormatVersion = 19;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
@@ -77,6 +80,7 @@
   V(AwaitExpression, 51)                                                       \
   V(FunctionExpression, 52)                                                    \
   V(Let, 53)                                                                   \
+  V(BlockExpression, 82)                                                       \
   V(Instantiation, 54)                                                         \
   V(PositiveIntLiteral, 55)                                                    \
   V(NegativeIntLiteral, 56)                                                    \