[vm] Skip bounds checks on invocation of partially instantiated closures.
Change-Id: I042607b6c255b20d8071dd51439a50bfd4c3bee7
Reviewed-on: https://dart-review.googlesource.com/72460
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 88cec81..7430fac 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -45,7 +45,6 @@
unbox_numeric_fields,
!USING_DBC,
"Support unboxed double and float32x4 fields.");
-DECLARE_FLAG(bool, eliminate_type_checks);
class SubclassFinder {
public:
diff --git a/runtime/vm/compiler/frontend/flow_graph_builder.cc b/runtime/vm/compiler/frontend/flow_graph_builder.cc
index 4d93fdf..847053c 100644
--- a/runtime/vm/compiler/frontend/flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/flow_graph_builder.cc
@@ -36,10 +36,6 @@
namespace dart {
-DEFINE_FLAG(bool,
- eliminate_type_checks,
- true,
- "Eliminate type checks when allowed by static type analysis.");
DEFINE_FLAG(bool, print_ast, false, "Print abstract syntax tree.");
DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables.");
DEFINE_FLAG(bool,
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ad4ad84..51d3b26 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -965,6 +965,7 @@
I->strong() && I->reify_generic_functions();
TypeParameter& forwarding_param = TypeParameter::Handle(Z);
+ Fragment check_bounds;
for (intptr_t i = 0; i < num_type_params; ++i) {
TypeParameterHelper helper(this);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
@@ -1009,7 +1010,20 @@
param ^= TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
}
ASSERT(param.IsFinalized());
- *implicit_checks += CheckTypeArgumentBound(param, bound, name);
+ check_bounds += CheckTypeArgumentBound(param, bound, name);
+ }
+
+ // Type arguments passed through partial instantiation are guaranteed to be
+ // bounds-checked at the point of partial instantiation, so we don't need to
+ // check them again at the call-site.
+ if (dart_function.IsClosureFunction() && !check_bounds.is_empty() &&
+ FLAG_eliminate_type_checks) {
+ LocalVariable* closure =
+ parsed_function()->node_sequence()->scope()->VariableAt(0);
+ *implicit_checks += B->TestDelayedTypeArgs(closure, /*present=*/{},
+ /*absent=*/check_bounds);
+ } else {
+ *implicit_checks += check_bounds;
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
@@ -4737,6 +4751,7 @@
// Create a copy of the closure.
+ Tag closure_expression_tag = PeekTag();
Fragment instructions = BuildExpression();
LocalVariable* original_closure = MakeTemporary();
@@ -4752,20 +4767,39 @@
// Check the bounds.
//
- // TODO(sjindel): Only perform this check for instance tearoffs, not for
- // tearoffs against local or top-level functions.
- instructions += LoadLocal(original_closure);
- instructions += PushArgument();
- instructions += LoadLocal(type_args_vec);
- instructions += PushArgument();
- const Library& dart_internal = Library::Handle(Z, Library::InternalLibrary());
- const Function& bounds_check_function = Function::ZoneHandle(
- Z, dart_internal.LookupFunctionAllowPrivate(
- Symbols::BoundsCheckForPartialInstantiation()));
- ASSERT(!bounds_check_function.IsNull());
- instructions += StaticCall(TokenPosition::kNoSource, bounds_check_function, 2,
- ICData::kStatic);
- instructions += Drop();
+ // The Dart 2.0 spec only allows the partial instantiation operation to happen
+ // against a "tearoff", which in the spec's language means a direct reference
+ // to a local function, a direct reference to a toplevel function, or an
+ // actual tearoff of a method against an object.
+ //
+ // In the first two cases the closure's expression will be represented by a
+ // "VariableGet" or "StaticGet", and in these cases we don't need to perform
+ // any checks, since the bounds are known statically by the front-end. This is
+ // also true also in the case of a super-tearoff or tearoff by direct method
+ // reference. Only in the latter case, where the closure's expression is
+ // represented by a "PropertyGet", is the target resolved dynamically, and
+ // checks are needed.
+ ASSERT(closure_expression_tag == kPropertyGet ||
+ closure_expression_tag == kDirectPropertyGet ||
+ closure_expression_tag == kSuperPropertyGet ||
+ closure_expression_tag == kVariableGet ||
+ closure_expression_tag == kSpecializedVariableGet ||
+ closure_expression_tag == kStaticGet);
+ if (closure_expression_tag == kPropertyGet || !FLAG_eliminate_type_checks) {
+ instructions += LoadLocal(original_closure);
+ instructions += PushArgument();
+ instructions += LoadLocal(type_args_vec);
+ instructions += PushArgument();
+ const Library& dart_internal =
+ Library::Handle(Z, Library::InternalLibrary());
+ const Function& bounds_check_function = Function::ZoneHandle(
+ Z, dart_internal.LookupFunctionAllowPrivate(
+ Symbols::BoundsCheckForPartialInstantiation()));
+ ASSERT(!bounds_check_function.IsNull());
+ instructions += StaticCall(TokenPosition::kNoSource, bounds_check_function,
+ 2, ICData::kStatic);
+ instructions += Drop();
+ }
instructions += LoadLocal(new_closure);
instructions += LoadLocal(type_args_vec);
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 83c1162..2f3600b 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -214,6 +214,8 @@
P(enable_multiple_entrypoints, bool, true, \
"Enable multiple entrypoints per-function and related optimizations.") \
R(enable_testing_pragmas, false, bool, false, \
- "Enable magical pragmas for testing purposes. Use at your own risk!")
+ "Enable magical pragmas for testing purposes. Use at your own risk!") \
+ R(eliminate_type_checks, true, bool, true, \
+ "Eliminate type checks when allowed by static type analysis.")
#endif // RUNTIME_VM_FLAG_LIST_H_
diff --git a/tests/language_2/language_2.status b/tests/language_2/language_2.status
index 6a491e4..48b19d7 100644
--- a/tests/language_2/language_2.status
+++ b/tests/language_2/language_2.status
@@ -39,6 +39,11 @@
stacktrace_demangle_ctors_test: SkipByDesign # Names are not scrubbed.
type_checks_in_factory_method_test: SkipByDesign # Requires checked mode.
+[ $fasta ]
+partial_instantiation_static_bounds_check_test/01: MissingCompileTimeError # Issue 34327
+partial_instantiation_static_bounds_check_test/02: MissingCompileTimeError # Issue 34327
+partial_instantiation_static_bounds_check_test/03: MissingCompileTimeError # Issue 34327
+
[ $compiler != app_jitk && $compiler != dartk && $compiler != dartkb && $compiler != dartkp && $mode == debug && $runtime == vm ]
built_in_identifier_type_annotation_test/set: Crash # Not supported by legacy VM front-end.
diff --git a/tests/language_2/partial_instantiation_static_bounds_check_test.dart b/tests/language_2/partial_instantiation_static_bounds_check_test.dart
new file mode 100644
index 0000000..599574d
--- /dev/null
+++ b/tests/language_2/partial_instantiation_static_bounds_check_test.dart
@@ -0,0 +1,24 @@
+topFn<T extends num>(T x) {
+ print(T);
+}
+
+class C<T> {
+ instanceFn<S extends T>(S x) {
+ print(S);
+ }
+}
+
+class D<T> extends C<T> {
+ void foo() {
+ void Function(int) k = instanceFn; //# 03: compile-time error
+ }
+}
+
+void main() {
+ localFn<T extends num>(T x) {
+ print(T);
+ }
+
+ void Function(String) k0 = localFn; //# 01: compile-time error
+ void Function(String) k1 = topFn; //# 02: compile-time error
+}