Revert 4f18af12c7c6d53f02cf32cb9b5ea848b86e1d77 as it causes test breakages.
Bug: https://github.com/dart-lang/sdk/issues/33040
Change-Id: I09aac75e440876111a2c91aaf728ba514d7d5c6b
Reviewed-on: https://dart-review.googlesource.com/53688
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index fe0711f..d586dc0 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -462,9 +462,29 @@
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_len, arguments->NativeArgAt(2));
const intptr_t len = smi_len.Value();
- return function_type_arguments.Prepend(zone, parent_type_arguments, len);
+ const TypeArguments& result =
+ TypeArguments::Handle(zone, TypeArguments::New(len, Heap::kNew));
+ AbstractType& type = AbstractType::Handle(zone);
+ const intptr_t split = parent_type_arguments.IsNull()
+ ? len - function_type_arguments.Length()
+ : parent_type_arguments.Length();
+ for (intptr_t i = 0; i < split; i++) {
+ type = parent_type_arguments.IsNull() ? Type::DynamicType()
+ : parent_type_arguments.TypeAt(i);
+ result.SetTypeAt(i, type);
+ }
+ for (intptr_t i = split; i < len; i++) {
+ type = function_type_arguments.IsNull()
+ ? Type::DynamicType()
+ : function_type_arguments.TypeAt(i - split);
+ result.SetTypeAt(i, type);
+ }
+ return result.Canonicalize();
}
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 9d3d889..c9044d3 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5010,10 +5010,66 @@
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 aa1cb20..1064875 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -111,15 +111,6 @@
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);
@@ -1339,52 +1330,6 @@
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 817633f..36557a1 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -7,8 +7,6 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
-#include <initializer_list>
-
#include "vm/growable_array.h"
#include "vm/hash_map.h"
@@ -171,12 +169,6 @@
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) {}
@@ -186,8 +178,6 @@
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);
@@ -641,13 +631,6 @@
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 902b5b0..66a82c9 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(nsm);
+ Fragment f = BuildTypeArgumentsHandling(strong);
if (link) prologue += f;
}
@@ -391,49 +391,36 @@
return populate_context;
}
-Fragment PrologueBuilder::BuildTypeArgumentsHandling(JoinEntryInstr* nsm) {
+Fragment PrologueBuilder::BuildTypeArgumentsHandling(bool strong) {
+ Fragment populate_args_desc;
+
LocalVariable* type_args_var = parsed_function_->RawTypeArgumentsVariable();
- Fragment handling;
+ 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(¬_passed, &passed);
- Fragment store_type_args;
+ JoinEntryInstr* join = BuildJoinEntry();
+
+ Fragment store_type_args(passed);
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;
+ Fragment store_null(not_passed);
store_null += NullConstant();
store_null += StoreLocal(TokenPosition::kNoSource, type_args_var);
store_null += Drop();
+ store_null += Goto(join);
- handling += TestTypeArgsLen(store_null, store_type_args, 0);
+ populate_args_desc = Fragment(populate_args_desc.entry, join);
- 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;
+ return populate_args_desc;
}
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 2e1026f..5e89638 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(JoinEntryInstr* nsm);
+ Fragment BuildTypeArgumentsHandling(bool strong);
LocalVariable* ParameterVariable(intptr_t index) {
return parsed_function_->RawParameterVariable(index);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 7c31cf4..ea2f2b2 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4856,28 +4856,6 @@
return result;
}
-RawTypeArguments* TypeArguments::Prepend(Zone* zone,
- const TypeArguments& other,
- intptr_t total_length) const {
- if (IsNull() && other.IsNull()) {
- return Object::null_type_arguments().raw();
- }
- const TypeArguments& result =
- TypeArguments::Handle(zone, TypeArguments::New(total_length, Heap::kNew));
- AbstractType& type = AbstractType::Handle(zone);
- const intptr_t split =
- other.IsNull() ? total_length - Length() : other.Length();
- for (intptr_t i = 0; i < split; i++) {
- type = other.IsNull() ? Type::DynamicType() : other.TypeAt(i);
- result.SetTypeAt(i, type);
- }
- for (intptr_t i = split; i < total_length; i++) {
- type = IsNull() ? Type::DynamicType() : TypeAt(i - split);
- result.SetTypeAt(i, type);
- }
- return result.Canonicalize();
-}
-
RawString* TypeArguments::SubvectorName(intptr_t from_index,
intptr_t len,
NameVisibility name_visibility) const {
@@ -22801,11 +22779,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 (delayed_type_args.raw() != Object::empty_type_arguments().raw()) {
+ if (sig_fun.IsImplicitClosureFunction() &&
+ delayed_type_args.raw() != Object::empty_type_arguments().raw()) {
num_free_params = kCurrentAndEnclosingFree;
- fn_type_args = delayed_type_args.Prepend(
- zone, fn_type_args,
- sig_fun.NumTypeParameters() + sig_fun.NumParentTypeParameters());
+ ASSERT(fn_type_args.IsNull()); // Implicit closure cannot have a parent.
+ fn_type_args = delayed_type_args.raw();
} else {
num_free_params = kAllFree;
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 60e0288..4bc1b9b 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5755,10 +5755,6 @@
return IsDynamicTypes(true, 0, len);
}
- RawTypeArguments* Prepend(Zone* zone,
- const TypeArguments& other,
- 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 254949e..1c50f7f 100644
--- a/tests/language_2/instantiate_tearoff_test.dart
+++ b/tests/language_2/instantiate_tearoff_test.dart
@@ -8,9 +8,10 @@
int intToInt(int x) => x;
String stringToString(String x) => x;
-String stringAndIntToString(String x, int y) => x;
-test(intFuncDynamic, stringFuncDynamic, dynamicFuncDynamic) {
+main() {
+ int Function(int) intFunc = f;
+ dynamic intFuncDynamic = intFunc;
Expect.isTrue(intFuncDynamic is int Function(int));
Expect.isFalse(intFuncDynamic is String Function(String));
Expect.equals(intFuncDynamic(1), 1);
@@ -21,6 +22,8 @@
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');
@@ -32,33 +35,9 @@
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 b58c2c8..8c9665a 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -598,6 +598,7 @@
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,6 +932,7 @@
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