Skip to content

Commit dbe868d

Browse files
sjindel-googlecommit-bot@chromium.org
authored andcommitted
[vm] Correct semantics of bounds-checks on partial instantiation.
Fixes #34267. Change-Id: Ieb557e186aa457bac3b99b6817050bde8f8e142f Reviewed-on: https://dart-review.googlesource.com/72161 Commit-Queue: Samir Jindel <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent 3603af3 commit dbe868d

8 files changed

+122
-4
lines changed

runtime/lib/internal_patch.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ class Lists {
102102
_prependTypeArguments(functionTypeArguments, parentTypeArguments, parentLen,
103103
totalLen) native "Internal_prependTypeArguments";
104104

105+
// Check that a set of type arguments satisfy the type parameter bounds on a
106+
// closure.
107+
@pragma("vm:entry-point")
108+
_boundsCheckForPartialInstantiation(closure, type_args)
109+
native "Internal_boundsCheckForPartialInstantiation";
110+
105111
// Called by IRRegExpMacroAssembler::GrowStack.
106112
Int32List _growRegExpStack(Int32List stack) {
107113
final newStack = new Int32List(stack.length * 2);

runtime/lib/object.cc

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,72 @@ DEFINE_NATIVE_ENTRY(Internal_prependTypeArguments, 4) {
473473
zone, parent_type_arguments, smi_parent_len.Value(), smi_len.Value());
474474
}
475475

476+
// Check that a set of type arguments satisfy the type parameter bounds on a
477+
// closure.
478+
// Arg0: Closure object
479+
// Arg1: Type arguments to function
480+
DEFINE_NATIVE_ENTRY(Internal_boundsCheckForPartialInstantiation, 2) {
481+
const Closure& closure =
482+
Closure::CheckedHandle(zone, arguments->NativeArgAt(0));
483+
const Function& target = Function::Handle(zone, closure.function());
484+
const TypeArguments& bounds =
485+
TypeArguments::Handle(zone, target.type_parameters());
486+
487+
// Either the bounds are all-dynamic or the function is not generic.
488+
if (bounds.IsNull()) return Object::null();
489+
490+
const TypeArguments& type_args_to_check =
491+
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
492+
493+
// This should be guaranteed by the front-end.
494+
ASSERT(type_args_to_check.IsNull() ||
495+
bounds.Length() == type_args_to_check.Length());
496+
497+
// The bounds on the closure may need instantiation.
498+
const TypeArguments& instantiator_type_args =
499+
TypeArguments::Handle(zone, closure.instantiator_type_arguments());
500+
const TypeArguments& function_type_args =
501+
TypeArguments::Handle(zone, closure.function_type_arguments());
502+
503+
AbstractType& supertype = AbstractType::Handle(zone);
504+
AbstractType& subtype = AbstractType::Handle(zone);
505+
TypeParameter& parameter = TypeParameter::Handle(zone);
506+
for (intptr_t i = 0; i < bounds.Length(); ++i) {
507+
parameter ^= bounds.TypeAt(i);
508+
supertype = parameter.bound();
509+
subtype = type_args_to_check.TypeAt(i);
510+
511+
ASSERT(!subtype.IsNull() && !subtype.IsMalformedOrMalbounded());
512+
ASSERT(!supertype.IsNull() && !supertype.IsMalformedOrMalbounded());
513+
514+
// The supertype may not be instantiated.
515+
Error& bound_error = Error::Handle(zone);
516+
if (!AbstractType::InstantiateAndTestSubtype(
517+
&subtype, &supertype, &bound_error, instantiator_type_args,
518+
function_type_args)) {
519+
// Throw a dynamic type error.
520+
TokenPosition location;
521+
{
522+
DartFrameIterator iterator(Thread::Current(),
523+
StackFrameIterator::kNoCrossThreadIteration);
524+
StackFrame* caller_frame = iterator.NextFrame();
525+
ASSERT(caller_frame != NULL);
526+
location = caller_frame->GetTokenPos();
527+
}
528+
String& bound_error_message = String::Handle(zone);
529+
if (!bound_error.IsNull()) {
530+
bound_error_message = String::New(bound_error.ToErrorCString());
531+
}
532+
String& parameter_name = String::Handle(zone, parameter.Name());
533+
Exceptions::CreateAndThrowTypeError(location, subtype, supertype,
534+
parameter_name, bound_error_message);
535+
UNREACHABLE();
536+
}
537+
}
538+
539+
return Object::null();
540+
}
541+
476542
DEFINE_NATIVE_ENTRY(InvocationMirror_unpackTypeArguments, 2) {
477543
const TypeArguments& type_arguments =
478544
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));

runtime/vm/bootstrap_natives.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ namespace dart {
313313
V(Internal_inquireIs64Bit, 0) \
314314
V(Internal_extractTypeArguments, 2) \
315315
V(Internal_prependTypeArguments, 4) \
316+
V(Internal_boundsCheckForPartialInstantiation, 2) \
316317
V(InvocationMirror_unpackTypeArguments, 2) \
317318
V(NoSuchMethodError_existingMethodSignature, 3) \
318319
V(LinkedHashMap_getIndex, 1) \

runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4745,14 +4745,35 @@ Fragment StreamingFlowGraphBuilder::BuildPartialTearoffInstantiation(
47454745
Class::ZoneHandle(Z, I->object_store()->closure_class()), 0);
47464746
LocalVariable* new_closure = MakeTemporary();
47474747

4748-
instructions += LoadLocal(new_closure);
4749-
47504748
intptr_t num_type_args = ReadListLength();
47514749
const TypeArguments& type_args = T.BuildTypeArguments(num_type_args);
47524750
instructions += TranslateInstantiatedTypeArguments(type_args);
4751+
LocalVariable* type_args_vec = MakeTemporary();
4752+
4753+
// Check the bounds.
4754+
//
4755+
// TODO(sjindel): Only perform this check for instance tearoffs, not for
4756+
// tearoffs against local or top-level functions.
4757+
instructions += LoadLocal(original_closure);
4758+
instructions += PushArgument();
4759+
instructions += LoadLocal(type_args_vec);
4760+
instructions += PushArgument();
4761+
const Library& dart_internal = Library::Handle(Z, Library::InternalLibrary());
4762+
const Function& bounds_check_function = Function::ZoneHandle(
4763+
Z, dart_internal.LookupFunctionAllowPrivate(
4764+
Symbols::BoundsCheckForPartialInstantiation()));
4765+
ASSERT(!bounds_check_function.IsNull());
4766+
instructions += StaticCall(TokenPosition::kNoSource, bounds_check_function, 2,
4767+
ICData::kStatic);
4768+
instructions += Drop();
4769+
4770+
instructions += LoadLocal(new_closure);
4771+
instructions += LoadLocal(type_args_vec);
47534772
instructions += StoreInstanceField(TokenPosition::kNoSource,
47544773
Closure::delayed_type_arguments_offset());
47554774

4775+
instructions += Drop(); // Drop type args.
4776+
47564777
// Copy over the target function.
47574778
instructions += LoadLocal(new_closure);
47584779
instructions += LoadLocal(original_closure);
@@ -4781,7 +4802,7 @@ Fragment StreamingFlowGraphBuilder::BuildPartialTearoffInstantiation(
47814802
instructions +=
47824803
StoreInstanceField(TokenPosition::kNoSource, Closure::context_offset());
47834804

4784-
instructions += DropTempsPreserveTop(1); // drop old closure
4805+
instructions += DropTempsPreserveTop(1); // Drop old closure.
47854806

47864807
return instructions;
47874808
}

runtime/vm/symbols.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ class ObjectPointerVisitor;
465465
V(vm_entry_point, "vm:entry-point") \
466466
V(Get, "get") \
467467
V(Set, "set") \
468-
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn")
468+
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
469+
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation")
469470

470471
// Contains a list of frequently used strings in a canonicalized form. This
471472
// list is kept in the vm_isolate in order to share the copy across isolates

tests/language_2/language_2_dart2js.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ mixin_type_parameter_inference_test/13: CompileTimeError
6767
mixin_type_parameter_inference_test/16: CompileTimeError
6868
mixin_type_parameter_inference_test/none: CompileTimeError
6969
number_identity_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
70+
partial_instantiation_eager_bounds_check_test: RuntimeError # Issue #34295
7071
partial_tearoff_instantiation_test/05: Pass # for the wrong reason.
7172
partial_tearoff_instantiation_test/06: Pass # for the wrong reason.
7273
partial_tearoff_instantiation_test/07: Pass # for the wrong reason.

tests/language_2/language_2_dartdevc.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ number_identity2_test: RuntimeError # Issue 29920; Expect.isTrue(false) fails.
495495
number_identity_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
496496
numbers_test: RuntimeError # Issue 29920; Expect.equals(expected: <false>, actual: <true>) fails.
497497
parser_quirks_test: CompileTimeError
498+
partial_instantiation_eager_bounds_check_test: RuntimeError # Issue 34296
498499
regress_16640_test: RuntimeError # Issue 29920; Uncaught Error: type arguments should not be null: E => {
499500
regress_22443_test: RuntimeError # Uncaught Expect.isTrue(false) fails.
500501
stack_overflow_stacktrace_test: RuntimeError # Issue 29920; RangeError: Maximum call stack size exceeded
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// This test checks that necessary type argument bounds checks are performed
6+
// eagerly during partial instantiation, rather than being delayed until the
7+
// partially instantiated closure is invoked.
8+
9+
import "package:expect/expect.dart";
10+
11+
class C<T> {
12+
void foo<S extends T>(S x) {}
13+
}
14+
15+
void main() {
16+
C<Object> c = C<int>();
17+
void Function(String) fn;
18+
Expect.throwsTypeError(() {
19+
fn = c.foo;
20+
});
21+
}

0 commit comments

Comments
 (0)