[vm/kernel] Re-land partial instantiation of local functions.

This change has been adjusted to account for the recent fix to Internal_prependTypeArguments.

Reviewed-on: https://dart-review.googlesource.com/50980
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Change-Id: I434a42578b91f0930b088c4d264d882c3ef5b4a3
Reviewed-on: https://dart-review.googlesource.com/53801
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index f47a80a..9008f08 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -462,28 +462,10 @@
       TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));
   const TypeArguments& parent_type_arguments =
       TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
-  if (function_type_arguments.IsNull() && parent_type_arguments.IsNull()) {
-    return TypeArguments::null();
-  }
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_parent_len, arguments->NativeArgAt(2));
-  const intptr_t parent_len = smi_parent_len.Value();
-  GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_total_len, arguments->NativeArgAt(3));
-  const intptr_t total_len = smi_total_len.Value();
-  const TypeArguments& result =
-      TypeArguments::Handle(zone, TypeArguments::New(total_len, Heap::kNew));
-  AbstractType& type = AbstractType::Handle(zone);
-  for (intptr_t i = 0; i < parent_len; i++) {
-    type = parent_type_arguments.IsNull() ? Type::DynamicType()
-                                          : parent_type_arguments.TypeAt(i);
-    result.SetTypeAt(i, type);
-  }
-  for (intptr_t i = parent_len; i < total_len; i++) {
-    type = function_type_arguments.IsNull()
-               ? Type::DynamicType()
-               : function_type_arguments.TypeAt(i - parent_len);
-    result.SetTypeAt(i, type);
-  }
-  return result.Canonicalize();
+  GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_len, arguments->NativeArgAt(3));
+  return function_type_arguments.Prepend(
+      zone, parent_type_arguments, smi_parent_len.Value(), smi_len.Value());
 }
 
 DEFINE_NATIVE_ENTRY(InvocationMirror_unpackTypeArguments, 1) {
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ee02cbc..b0cea33 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5010,66 +5010,10 @@
   body +=
       flow_graph_builder_->CheckStackOverflowInPrologue(function.token_pos());
 
-  // Forwarding the type parameters is complicated by our approach to
-  // implementing the partial tearoff instantiation.
-  //
-  // When a tearoff is partially applied to a set of type arguments, the type
-  // arguments are saved in the closure's "delayed_type_arguments" field. The
-  // partial type application operator is guaranteed to provide arguments for
-  // all of a generic tearoff's type parameters, so we will only have to forward
-  // type arguments from the caller or from the closure object. Therefore, if
-  // there are type arguments saved on the tearoff and type arguments are
-  // passed, we must throw NoSuchMethod.
   intptr_t type_args_len = 0;
   if (I->reify_generic_functions() && function.IsGeneric()) {
+    type_args_len = function.NumTypeParameters();
     ASSERT(parsed_function()->function_type_arguments() != NULL);
-    type_args_len = target.NumTypeParameters();
-
-    Fragment copy_type_args;
-
-    LocalVariable* closure =
-        parsed_function()->node_sequence()->scope()->VariableAt(0);
-    copy_type_args += LoadLocal(closure);
-    copy_type_args += LoadField(Closure::delayed_type_arguments_offset());
-
-    TargetEntryInstr *has_delayed_type_args, *no_delayed_type_args;
-    copy_type_args += Constant(Object::empty_type_arguments());
-    copy_type_args += BranchIfEqual(&no_delayed_type_args,
-                                    &has_delayed_type_args, /*negate=*/false);
-    JoinEntryInstr* join = BuildJoinEntry();
-
-    // We found type arguments saved on the tearoff to be provided to the
-    // function.
-
-    Fragment copy_delayed_type_args(has_delayed_type_args);
-
-    copy_delayed_type_args +=
-        LoadLocal(parsed_function()->function_type_arguments());
-
-    TargetEntryInstr *no_type_args, *passed_type_args;
-    copy_delayed_type_args +=
-        BranchIfNull(&no_type_args, &passed_type_args, /*negate=*/false);
-
-    Fragment use_instantiated_args(no_type_args);
-    use_instantiated_args += LoadLocal(closure);
-    use_instantiated_args +=
-        LoadField(Closure::delayed_type_arguments_offset());
-
-    // Prepending of captured type arguments will happen in the target.
-    use_instantiated_args += StoreLocal(
-        TokenPosition::kNoSource, parsed_function()->function_type_arguments());
-    use_instantiated_args += Drop();
-    use_instantiated_args += Goto(join);
-
-    Fragment goto_nsm(passed_type_args);
-    goto_nsm += Goto(flow_graph_builder_->BuildThrowNoSuchMethod());
-
-    // The tearoff was not partially applied, so we forward type arguments from
-    // the caller.
-    Fragment forward_caller_type_args(no_delayed_type_args);
-    forward_caller_type_args += Goto(join);
-
-    body += Fragment(copy_type_args.entry, join);
     body += LoadLocal(parsed_function()->function_type_arguments());
     body += PushArgument();
   }
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 1064875..aa1cb20 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -111,6 +111,15 @@
   return *this;
 }
 
+void Fragment::Prepend(Instruction* start) {
+  if (entry == NULL) {
+    entry = current = start;
+  } else {
+    start->LinkTo(entry);
+    entry = start;
+  }
+}
+
 Fragment Fragment::closed() {
   ASSERT(entry != NULL);
   return Fragment(entry, NULL);
@@ -1330,6 +1339,52 @@
   return Fragment(new (Z) TailCallInstr(code, arg_desc));
 }
 
+Fragment BaseFlowGraphBuilder::TestTypeArgsLen(Fragment eq_branch,
+                                               Fragment neq_branch,
+                                               intptr_t num_type_args) {
+  Fragment test;
+
+  TargetEntryInstr* eq_entry;
+  TargetEntryInstr* neq_entry;
+
+  test += LoadArgDescriptor();
+  test += LoadField(ArgumentsDescriptor::type_args_len_offset());
+  test += IntConstant(num_type_args);
+  test += BranchIfEqual(&eq_entry, &neq_entry);
+
+  eq_branch.Prepend(eq_entry);
+  neq_branch.Prepend(neq_entry);
+
+  JoinEntryInstr* join = BuildJoinEntry();
+  eq_branch += Goto(join);
+  neq_branch += Goto(join);
+
+  return Fragment(test.entry, join);
+}
+
+Fragment BaseFlowGraphBuilder::TestDelayedTypeArgs(LocalVariable* closure,
+                                                   Fragment present,
+                                                   Fragment absent) {
+  Fragment test;
+
+  TargetEntryInstr* absent_entry;
+  TargetEntryInstr* present_entry;
+
+  test += LoadLocal(closure);
+  test += LoadField(Closure::delayed_type_arguments_offset());
+  test += Constant(Object::empty_type_arguments());
+  test += BranchIfEqual(&absent_entry, &present_entry);
+
+  present.Prepend(present_entry);
+  absent.Prepend(absent_entry);
+
+  JoinEntryInstr* join = BuildJoinEntry();
+  absent += Goto(join);
+  present += Goto(join);
+
+  return Fragment(test.entry, join);
+}
+
 Fragment FlowGraphBuilder::RethrowException(TokenPosition position,
                                             int catch_try_index) {
   Fragment instructions;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 36557a1..817633f 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -7,6 +7,8 @@
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
+#include <initializer_list>
+
 #include "vm/growable_array.h"
 #include "vm/hash_map.h"
 
@@ -169,6 +171,12 @@
 
   Fragment() : entry(NULL), current(NULL) {}
 
+  Fragment(std::initializer_list<Fragment> list) : entry(NULL), current(NULL) {
+    for (Fragment i : list) {
+      *this += i;
+    }
+  }
+
   explicit Fragment(Instruction* instruction)
       : entry(instruction), current(instruction) {}
 
@@ -178,6 +186,8 @@
   bool is_open() { return entry == NULL || current != NULL; }
   bool is_closed() { return !is_open(); }
 
+  void Prepend(Instruction* start);
+
   Fragment& operator+=(const Fragment& other);
   Fragment& operator<<=(Instruction* next);
 
@@ -631,6 +641,13 @@
     return LoadLocal(parsed_function_->arg_desc_var());
   }
 
+  Fragment TestTypeArgsLen(Fragment eq_branch,
+                           Fragment neq_branch,
+                           intptr_t num_type_args);
+  Fragment TestDelayedTypeArgs(LocalVariable* closure,
+                               Fragment present,
+                               Fragment absent);
+
   JoinEntryInstr* BuildThrowNoSuchMethod();
 
  protected:
diff --git a/runtime/vm/compiler/frontend/prologue_builder.cc b/runtime/vm/compiler/frontend/prologue_builder.cc
index 66a82c9..902b5b0 100644
--- a/runtime/vm/compiler/frontend/prologue_builder.cc
+++ b/runtime/vm/compiler/frontend/prologue_builder.cc
@@ -58,7 +58,7 @@
     if (!compiling_for_osr_) prologue += f;
   }
   if (expect_type_args) {
-    Fragment f = BuildTypeArgumentsHandling(strong);
+    Fragment f = BuildTypeArgumentsHandling(nsm);
     if (link) prologue += f;
   }
 
@@ -391,36 +391,49 @@
   return populate_context;
 }
 
-Fragment PrologueBuilder::BuildTypeArgumentsHandling(bool strong) {
-  Fragment populate_args_desc;
-
+Fragment PrologueBuilder::BuildTypeArgumentsHandling(JoinEntryInstr* nsm) {
   LocalVariable* type_args_var = parsed_function_->RawTypeArgumentsVariable();
 
-  TargetEntryInstr *passed, *not_passed;
-  populate_args_desc += LoadArgDescriptor();
-  populate_args_desc += LoadField(ArgumentsDescriptor::type_args_len_offset());
-  populate_args_desc += IntConstant(0);
-  populate_args_desc += BranchIfEqual(&not_passed, &passed);
+  Fragment handling;
 
-  JoinEntryInstr* join = BuildJoinEntry();
-
-  Fragment store_type_args(passed);
+  Fragment store_type_args;
   store_type_args += LoadArgDescriptor();
   store_type_args += LoadField(ArgumentsDescriptor::count_offset());
   store_type_args += LoadFpRelativeSlot(kWordSize * (1 + kParamEndSlotFromFp));
   store_type_args += StoreLocal(TokenPosition::kNoSource, type_args_var);
   store_type_args += Drop();
-  store_type_args += Goto(join);
 
-  Fragment store_null(not_passed);
+  Fragment store_null;
   store_null += NullConstant();
   store_null += StoreLocal(TokenPosition::kNoSource, type_args_var);
   store_null += Drop();
-  store_null += Goto(join);
 
-  populate_args_desc = Fragment(populate_args_desc.entry, join);
+  handling += TestTypeArgsLen(store_null, store_type_args, 0);
 
-  return populate_args_desc;
+  if (parsed_function_->function().IsClosureFunction()) {
+    LocalVariable* closure =
+        parsed_function_->node_sequence()->scope()->VariableAt(0);
+
+    // Currently, delayed type arguments can only be introduced through type
+    // inference in the FE. So if they are present, we can assume they are
+    // correct in number and bound.
+    // clang-format off
+    Fragment use_delayed_type_args = {
+      LoadLocal(closure),
+      LoadField(Closure::delayed_type_arguments_offset()),
+      StoreLocal(TokenPosition::kNoSource, type_args_var),
+      Drop()
+    };
+
+    handling += TestDelayedTypeArgs(
+        closure,
+        /*present=*/TestTypeArgsLen(
+            use_delayed_type_args, Goto(nsm), 0),
+        /*absent=*/Fragment());
+    // clang-format on
+  }
+
+  return handling;
 }
 
 void PrologueBuilder::SortOptionalNamedParametersInto(LocalVariable** opt_param,
diff --git a/runtime/vm/compiler/frontend/prologue_builder.h b/runtime/vm/compiler/frontend/prologue_builder.h
index 5e89638..2e1026f 100644
--- a/runtime/vm/compiler/frontend/prologue_builder.h
+++ b/runtime/vm/compiler/frontend/prologue_builder.h
@@ -60,7 +60,7 @@
 
   Fragment BuildClosureContextHandling();
 
-  Fragment BuildTypeArgumentsHandling(bool strong);
+  Fragment BuildTypeArgumentsHandling(JoinEntryInstr* nsm);
 
   LocalVariable* ParameterVariable(intptr_t index) {
     return parsed_function_->RawParameterVariable(index);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ea2f2b2..94ce9ee 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4856,6 +4856,27 @@
   return result;
 }
 
+RawTypeArguments* TypeArguments::Prepend(Zone* zone,
+                                         const TypeArguments& other,
+                                         intptr_t other_length,
+                                         intptr_t total_length) const {
+  if (IsNull() && other.IsNull()) {
+    return TypeArguments::null();
+  }
+  const TypeArguments& result =
+      TypeArguments::Handle(zone, TypeArguments::New(total_length, Heap::kNew));
+  AbstractType& type = AbstractType::Handle(zone);
+  for (intptr_t i = 0; i < other_length; i++) {
+    type = other.IsNull() ? Type::DynamicType() : other.TypeAt(i);
+    result.SetTypeAt(i, type);
+  }
+  for (intptr_t i = other_length; i < total_length; i++) {
+    type = IsNull() ? Type::DynamicType() : TypeAt(i - other_length);
+    result.SetTypeAt(i, type);
+  }
+  return result.Canonicalize();
+}
+
 RawString* TypeArguments::SubvectorName(intptr_t from_index,
                                         intptr_t len,
                                         NameVisibility name_visibility) const {
@@ -22779,11 +22800,11 @@
   // We detect the case of a partial tearoff type application and substitute the
   // type arguments for the type parameters of the function.
   intptr_t num_free_params;
-  if (sig_fun.IsImplicitClosureFunction() &&
-      delayed_type_args.raw() != Object::empty_type_arguments().raw()) {
+  if (delayed_type_args.raw() != Object::empty_type_arguments().raw()) {
     num_free_params = kCurrentAndEnclosingFree;
-    ASSERT(fn_type_args.IsNull());  // Implicit closure cannot have a parent.
-    fn_type_args = delayed_type_args.raw();
+    fn_type_args = delayed_type_args.Prepend(
+        zone, fn_type_args, sig_fun.NumParentTypeParameters(),
+        sig_fun.NumTypeParameters() + sig_fun.NumParentTypeParameters());
   } else {
     num_free_params = kAllFree;
   }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 4bc1b9b..9787922 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5755,6 +5755,11 @@
     return IsDynamicTypes(true, 0, len);
   }
 
+  RawTypeArguments* Prepend(Zone* zone,
+                            const TypeArguments& other,
+                            intptr_t other_length,
+                            intptr_t total_length) const;
+
   // Check if the subvector of length 'len' starting at 'from_index' of this
   // type argument vector consists solely of DynamicType, ObjectType, or
   // VoidType.
diff --git a/tests/language_2/instantiate_tearoff_test.dart b/tests/language_2/instantiate_tearoff_test.dart
index 1c50f7f..6a551f6 100644
--- a/tests/language_2/instantiate_tearoff_test.dart
+++ b/tests/language_2/instantiate_tearoff_test.dart
@@ -8,10 +8,9 @@
 
 int intToInt(int x) => x;
 String stringToString(String x) => x;
+String stringAndIntToString(String x, int y) => x;
 
-main() {
-  int Function(int) intFunc = f;
-  dynamic intFuncDynamic = intFunc;
+test(intFuncDynamic, stringFuncDynamic, dynamicFuncDynamic) {
   Expect.isTrue(intFuncDynamic is int Function(int));
   Expect.isFalse(intFuncDynamic is String Function(String));
   Expect.equals(intFuncDynamic(1), 1);
@@ -22,8 +21,6 @@
   Expect.throwsNoSuchMethodError(() {
     intFuncDynamic<String>('oops');
   });
-  String Function(String) stringFunc = f;
-  dynamic stringFuncDynamic = stringFunc;
   Expect.isTrue(stringFuncDynamic is String Function(String));
   Expect.isFalse(stringFuncDynamic is int Function(int));
   Expect.equals(stringFuncDynamic('hello'), 'hello');
@@ -35,9 +32,34 @@
   Expect.throwsNoSuchMethodError(() {
     stringFuncDynamic<int>(1);
   });
-  dynamic Function(dynamic) dynamicFunc = f;
-  dynamic dynamicFuncDynamic = dynamicFunc;
   Expect.throwsNoSuchMethodError(() {
     dynamicFuncDynamic<int>(1);
   });
 }
+
+main() {
+  int Function(int) if1 = f;
+  String Function(String) sf1 = f;
+  dynamic Function(dynamic) df1 = f;
+  test(if1, sf1, df1);
+
+  T local<T>(T x) => x;
+
+  int Function(int) if2 = local;
+  String Function(String) sf2 = local;
+  dynamic Function(dynamic) df2 = local;
+  test(if2, sf2, df2);
+
+  dynamic bar<X>() {
+    String foo<T>(X x, T t) {
+      return "$X, $T";
+    }
+
+    String Function(X, int) x = foo;
+    return x;
+  }
+
+  dynamic fn = bar<String>();
+  Expect.equals("${fn.runtimeType}", "${stringAndIntToString.runtimeType}");
+  Expect.equals(fn("a", 1), "String, int");
+}
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index c76e229a..f53f4b5 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -598,7 +598,6 @@
 for_in_side_effects_test/01: MissingCompileTimeError
 function_propagation_test: RuntimeError
 function_subtype_inline2_test: RuntimeError
-generic_function_dcall_test: RuntimeError
 generic_instanceof2_test: RuntimeError
 generic_is_check_test: RuntimeError
 generic_no_such_method_dispatcher_simple_test: CompileTimeError # Issue 31533
@@ -931,7 +930,6 @@
 for_in_side_effects_test/01: MissingCompileTimeError
 function_propagation_test: RuntimeError
 function_subtype_inline2_test: RuntimeError
-generic_function_dcall_test: RuntimeError
 generic_instanceof2_test: RuntimeError
 generic_is_check_test: RuntimeError
 generic_methods_recursive_bound_test/03: Crash, Pass
diff --git a/tests/language_2/language_2_vm.status b/tests/language_2/language_2_vm.status
index e71bf72..9d7067e 100644
--- a/tests/language_2/language_2_vm.status
+++ b/tests/language_2/language_2_vm.status
@@ -1135,6 +1135,7 @@
 function_type_alias6_test/none: RuntimeError
 invalid_override_in_mixin_test/01: MissingCompileTimeError
 type_literal_prefix_call_test: RuntimeError
+vm/regress_33040_instantiation_test: RuntimeError
 
 # The VM and does not implement the Dart 2.0 runtime checks yet unless
 # --checked is explicitly passed).
diff --git a/tests/language_2/vm/regress_33040_instantiation_test.dart b/tests/language_2/vm/regress_33040_instantiation_test.dart
new file mode 100644
index 0000000..db536bb
--- /dev/null
+++ b/tests/language_2/vm/regress_33040_instantiation_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Exact regression test for issue #33040.
+
+import 'dart:async';
+
+class Optional<T> {}
+
+typedef T ConvertFunction<T>();
+
+T blockingLatest<T>() {
+  return null;
+}
+
+abstract class ObservableModel<T> {}
+
+abstract class AsyncValueMixin<T> {
+  Future<T> get asyncValue {
+    ConvertFunction<T> f = blockingLatest;
+    (f as ConvertFunction<T>);
+    return null;
+  }
+}
+
+abstract class OptionalSettableObservableModel<T>
+    extends ObservableModel<Optional<T>> with AsyncValueMixin<Optional<T>> {}
+
+class FooObservableModel extends OptionalSettableObservableModel<int> {}
+
+void main() async {
+  var model = new FooObservableModel();
+  final value = await model.asyncValue;
+}