Add partial instantiation support for constant evaluator [PartialInstantiationConstant]

Change-Id: Iaa93da60331737aaa6bceb4d2fe1d791dc7e95e9
Reviewed-on: https://dart-review.googlesource.com/52445
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 0cfe661..f87de3d 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -902,11 +902,22 @@
   List<[FieldReference, ConstantReference]> values;
 }
 
-type TearOffConstant extends Constant {
+type PartialInstantiationConstant extends Constant {
   Byte tag = 8;
+  ConstantReference tearOffConstant;
+  List<DartType> typeArguments;
+}
+
+type TearOffConstant extends Constant {
+  Byte tag = 9;
   CanonicalNameReference staticProcedureReference;
 }
 
+type TypeLiteralConstant extends Constant {
+  Byte tag = 10;
+  DartType type;
+}
+
 abstract type Statement extends Node {}
 
 type ExpressionStatement extends Statement {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index d4983da..ce95e14 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -5386,6 +5386,34 @@
   }
 }
 
+class PartialInstantiationConstant extends Constant {
+  final TearOffConstant tearOffConstant;
+  final List<DartType> types;
+
+  PartialInstantiationConstant(this.tearOffConstant, this.types);
+
+  visitChildren(Visitor v) {
+    tearOffConstant.acceptReference(v);
+    visitList(types, v);
+  }
+
+  accept(ConstantVisitor v) => v.visitPartialInstantiationConstant(this);
+  acceptReference(Visitor v) =>
+      v.visitPartialInstantiationConstantReference(this);
+
+  String toString() {
+    return '${runtimeType}(${tearOffConstant.procedure}<${types.join(', ')}>)';
+  }
+
+  int get hashCode => tearOffConstant.hashCode ^ listHashCode(types);
+
+  bool operator ==(Object other) {
+    return other is PartialInstantiationConstant &&
+        other.tearOffConstant == tearOffConstant &&
+        listEquals(other.types, types);
+  }
+}
+
 class TearOffConstant extends Constant {
   final Reference procedureReference;
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 6749c74..bfdf151 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -215,6 +215,14 @@
           fieldValues[fieldRef] = constant;
         }
         return new InstanceConstant(classReference, typeArguments, fieldValues);
+      case ConstantTag.PartialInstantiationConstant:
+        final tearOffConstant = readConstantReference() as TearOffConstant;
+        final int length = readUInt();
+        final List<DartType> types = new List<DartType>(length);
+        for (int i = 0; i < length; i++) {
+          types[i] = readDartType();
+        }
+        return new PartialInstantiationConstant(tearOffConstant, types);
       case ConstantTag.TearOffConstant:
         final Reference reference = readCanonicalNameReference().getReference();
         return new TearOffConstant.byReference(reference);
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 6d4c937..f54d8bc 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -175,6 +175,14 @@
         writeCanonicalNameReference(fieldRef.canonicalName);
         writeConstantReference(value);
       });
+    } else if (constant is PartialInstantiationConstant) {
+      writeByte(ConstantTag.PartialInstantiationConstant);
+      writeConstantReference(constant.tearOffConstant);
+      final int length = constant.types.length;
+      writeUInt30(length);
+      for (int i = 0; i < length; ++i) {
+        writeDartType(constant.types[i]);
+      }
     } else if (constant is TearOffConstant) {
       writeByte(ConstantTag.TearOffConstant);
       writeCanonicalNameReference(constant.procedure.canonicalName);
@@ -1897,6 +1905,19 @@
   }
 
   @override
+  void visitPartialInstantiationConstant(PartialInstantiationConstant node) {
+    throw new UnsupportedError(
+        'serialization of PartialInstantiationConstants ');
+  }
+
+  @override
+  void visitPartialInstantiationConstantReference(
+      PartialInstantiationConstant node) {
+    throw new UnsupportedError(
+        'serialization of PartialInstantiationConstant references');
+  }
+
+  @override
   void visitTearOffConstant(TearOffConstant node) {
     throw new UnsupportedError('serialization of TearOffConstants ');
   }
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index e55935e..bc70203 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -147,6 +147,7 @@
   static const int MapConstant = 5;
   static const int ListConstant = 6;
   static const int InstanceConstant = 7;
-  static const int TearOffConstant = 8;
-  static const int TypeLiteralConstant = 9;
+  static const int PartialInstantiationConstant = 8;
+  static const int TearOffConstant = 9;
+  static const int TypeLiteralConstant = 10;
 }
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index 84ae345..c618d81 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -729,6 +729,22 @@
     return canonicalize(backend.buildSymbolConstant(value));
   }
 
+  visitInstantiation(Instantiation node) {
+    final Constant constant = evaluate(node.expression);
+    if (constant is TearOffConstant) {
+      if (node.typeArguments.length ==
+          constant.procedure.function.typeParameters.length) {
+        return canonicalize(
+            new PartialInstantiationConstant(constant, node.typeArguments));
+      }
+      throw new ConstantEvaluationError(
+          'The number of type arguments supplied in the partial instantiation '
+          'does not match the number of type arguments of the $constant.');
+    }
+    throw new ConstantEvaluationError(
+        'Only tear-off constants can be partially instantiated.');
+  }
+
   // Helper methods:
 
   void ensureIsSubtype(Constant constant, DartType type) {
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index fa6337c..cde0c92 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -283,6 +283,8 @@
   R visitMapConstant(MapConstant node) => defaultConstant(node);
   R visitListConstant(ListConstant node) => defaultConstant(node);
   R visitInstanceConstant(InstanceConstant node) => defaultConstant(node);
+  R visitPartialInstantiationConstant(PartialInstantiationConstant node) =>
+      defaultConstant(node);
   R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
   R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
 }
@@ -334,6 +336,8 @@
   R visitMapConstant(MapConstant node) => defaultConstant(node);
   R visitListConstant(ListConstant node) => defaultConstant(node);
   R visitInstanceConstant(InstanceConstant node) => defaultConstant(node);
+  R visitPartialInstantiationConstant(PartialInstantiationConstant node) =>
+      defaultConstant(node);
   R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
   R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
 
@@ -359,6 +363,9 @@
       defaultConstantReference(node);
   R visitInstanceConstantReference(InstanceConstant node) =>
       defaultConstantReference(node);
+  R visitPartialInstantiationConstantReference(
+          PartialInstantiationConstant node) =>
+      defaultConstantReference(node);
   R visitTearOffConstantReference(TearOffConstant node) =>
       defaultConstantReference(node);
   R visitTypeLiteralConstantReference(TypeLiteralConstant node) =>
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 4b86b49..157e8186 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -10884,6 +10884,35 @@
         temp_instance_ = H.Canonicalize(temp_instance_);
         break;
       }
+      case kPartialInstantiationConstant: {
+        const intptr_t entry_index = builder_.ReadUInt();
+        temp_object_ = constants.At(entry_index);
+
+        const intptr_t number_of_type_arguments = builder_.ReadUInt();
+        if (temp_class_.NumTypeArguments() > 0) {
+          temp_type_arguments_ =
+              TypeArguments::New(number_of_type_arguments, Heap::kOld);
+          for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
+            temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType());
+          }
+        } else {
+          ASSERT(number_of_type_arguments == 0);
+          temp_type_arguments_ = TypeArguments::null();
+        }
+
+        // Make a copy of the old closure, with the delayed type arguments
+        // set to [temp_type_arguments_].
+        temp_closure_ = Closure::RawCast(temp_object_.raw());
+        temp_function_ = temp_closure_.function();
+        temp_type_arguments2_ = temp_closure_.instantiator_type_arguments();
+        temp_type_arguments3_ = temp_closure_.function_type_arguments();
+        temp_context_ = temp_closure_.context();
+        temp_closure_ = Closure::New(
+            temp_type_arguments2_, Object::null_type_arguments(),
+            temp_type_arguments_, temp_function_, temp_context_, Heap::kOld);
+        temp_instance_ = H.Canonicalize(temp_closure_);
+        break;
+      }
       case kTearOffConstant: {
         const NameIndex index = builder_.ReadCanonicalNameReference();
         NameIndex lib_index = index;
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index 337c045d6..0396038 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -1680,12 +1680,16 @@
         zone_(zone),
         temp_type_(AbstractType::Handle(zone)),
         temp_type_arguments_(TypeArguments::Handle(zone)),
+        temp_type_arguments2_(TypeArguments::Handle(zone)),
+        temp_type_arguments3_(TypeArguments::Handle(zone)),
         temp_object_(Object::Handle(zone)),
         temp_array_(Array::Handle(zone)),
         temp_instance_(Instance::Handle(zone)),
         temp_field_(Field::Handle(zone)),
         temp_class_(Class::Handle(zone)),
         temp_function_(Function::Handle(zone)),
+        temp_closure_(Closure::Handle(zone)),
+        temp_context_(Context::Handle(zone)),
         temp_integer_(Integer::Handle(zone)) {}
 
   // Reads the constant table from the binary.
@@ -1707,12 +1711,16 @@
   Zone* zone_;
   AbstractType& temp_type_;
   TypeArguments& temp_type_arguments_;
+  TypeArguments& temp_type_arguments2_;
+  TypeArguments& temp_type_arguments3_;
   Object& temp_object_;
   Array& temp_array_;
   Instance& temp_instance_;
   Field& temp_field_;
   Class& temp_class_;
   Function& temp_function_;
+  Closure& temp_closure_;
+  Context& temp_context_;
   Integer& temp_integer_;
 };
 
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index a5521fc..e426f4d 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -149,8 +149,9 @@
   kMapConstant = 5,
   kListConstant = 6,
   kInstanceConstant = 7,
-  kTearOffConstant = 8,
-  kTypeLiteralConstant = 9,
+  kPartialInstantiationConstant = 8,
+  kTearOffConstant = 9,
+  kTypeLiteralConstant = 10,
 };
 
 static const int SpecializedIntLiteralBias = 3;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c856133..f592c8c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -22765,6 +22765,16 @@
                          const Function& function,
                          const Context& context,
                          Heap::Space space) {
+  return Closure::New(instantiator_type_arguments, function_type_arguments,
+                      Object::empty_type_arguments(), function, context, space);
+}
+
+RawClosure* Closure::New(const TypeArguments& instantiator_type_arguments,
+                         const TypeArguments& function_type_arguments,
+                         const TypeArguments& delayed_type_arguments,
+                         const Function& function,
+                         const Context& context,
+                         Heap::Space space) {
   Closure& result = Closure::Handle();
   {
     RawObject* raw =
@@ -22776,7 +22786,7 @@
     result.StorePointer(&result.raw_ptr()->function_type_arguments_,
                         function_type_arguments.raw());
     result.StorePointer(&result.raw_ptr()->delayed_type_arguments_,
-                        Object::empty_type_arguments().raw());
+                        delayed_type_arguments.raw());
     result.StorePointer(&result.raw_ptr()->function_, function.raw());
     result.StorePointer(&result.raw_ptr()->context_, context.raw());
   }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index ea2c4d5..9b2c77e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -8791,6 +8791,13 @@
                          const Context& context,
                          Heap::Space space = Heap::kNew);
 
+  static RawClosure* New(const TypeArguments& instantiator_type_arguments,
+                         const TypeArguments& function_type_arguments,
+                         const TypeArguments& delayed_type_arguments,
+                         const Function& function,
+                         const Context& context,
+                         Heap::Space space = Heap::kNew);
+
   RawFunction* GetInstantiatedSignature(Zone* zone) const;
 
  private: