Skip to content

Commit eec08e1

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm] Support tear-offs for generative and factory constructors
TEST=co19/LanguageFeatures/Constructor-tear-offs Issue: #46231 Change-Id: I177d0d77eac32b49d39949d696de8aa9a618cc6c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208442 Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent d63c2df commit eec08e1

File tree

11 files changed

+157
-55
lines changed

11 files changed

+157
-55
lines changed

pkg/front_end/testcases/strong.status

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854
1010

1111
constructor_tearoffs/call_instantiation: TypeCheckError
1212
constructor_tearoffs/const_tear_off: RuntimeError
13-
constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
14-
constructor_tearoffs/inferred_non_proper_rename: RuntimeError
1513
constructor_tearoffs/redirecting_constructors: RuntimeError
1614
constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
1715
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.

pkg/front_end/testcases/text_serialization.status

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
constructor_tearoffs/call_instantiation: TypeCheckError
1010
constructor_tearoffs/const_tear_off: RuntimeError
11-
constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
12-
constructor_tearoffs/inferred_non_proper_rename: RuntimeError
1311
constructor_tearoffs/redirecting_constructors: RuntimeError
1412
constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
1513
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.

pkg/front_end/testcases/weak.status

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854
1313

1414
constructor_tearoffs/call_instantiation: TypeCheckError
1515
constructor_tearoffs/const_tear_off: RuntimeError
16-
constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
17-
constructor_tearoffs/inferred_non_proper_rename: RuntimeError
1816
constructor_tearoffs/redirecting_constructors: RuntimeError
1917
constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
2018
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.

pkg/vm/lib/transformations/type_flow/summary_collector.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,6 +2479,15 @@ class ConstantAllocationCollector extends ConstantVisitor<Type> {
24792479

24802480
@override
24812481
Type visitStaticTearOffConstant(StaticTearOffConstant constant) {
2482+
final Procedure member = constant.target;
2483+
summaryCollector._entryPointsListener
2484+
.addRawCall(new DirectSelector(member));
2485+
summaryCollector._entryPointsListener.recordTearOff(member);
2486+
return _getStaticType(constant);
2487+
}
2488+
2489+
@override
2490+
Type visitConstructorTearOffConstant(ConstructorTearOffConstant constant) {
24822491
final Member member = constant.target;
24832492
summaryCollector._entryPointsListener
24842493
.addRawCall(new DirectSelector(member));

pkg/vm/lib/transformations/type_flow/transformer.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,11 @@ class _TreeShakerConstantVisitor extends ConstantVisitor<Null> {
19381938
shaker.addUsedMember(constant.target);
19391939
}
19401940

1941+
@override
1942+
visitConstructorTearOffConstant(ConstructorTearOffConstant constant) {
1943+
shaker.addUsedMember(constant.target);
1944+
}
1945+
19411946
@override
19421947
visitInstantiationConstant(InstantiationConstant constant) {
19431948
analyzeConstant(constant.tearOffConstant);

runtime/vm/compiler/frontend/constant_reader.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,18 @@ InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_index) {
353353
instance = function.ImplicitStaticClosure();
354354
break;
355355
}
356+
case kConstructorTearOffConstant: {
357+
const NameIndex index = reader.ReadCanonicalNameReference();
358+
Function& function = Function::Handle(Z);
359+
if (H.IsConstructor(index)) {
360+
function = H.LookupConstructorByKernelConstructor(index);
361+
} else {
362+
function = H.LookupStaticMethodByKernelProcedure(index);
363+
}
364+
function = function.ImplicitClosureFunction();
365+
instance = function.ImplicitStaticClosure();
366+
break;
367+
}
356368
case kTypeLiteralConstant: {
357369
// Build type from the raw bytes (needs temporary translator).
358370
// Const canonical type erasure is not applied to constant type literals.

runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5299,7 +5299,9 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
52995299
if (!closure_owner_.IsNull()) {
53005300
function = Function::NewClosureFunctionWithKind(
53015301
UntaggedFunction::kClosureFunction, *name,
5302-
parsed_function()->function(), position, closure_owner_);
5302+
parsed_function()->function(),
5303+
parsed_function()->function().is_static(), position,
5304+
closure_owner_);
53035305
} else {
53045306
function = Function::NewClosureFunction(
53055307
*name, parsed_function()->function(), position);

runtime/vm/compiler/frontend/kernel_to_il.cc

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3425,13 +3425,45 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
34253425

34263426
intptr_t type_args_len = 0;
34273427
if (function.IsGeneric()) {
3428-
type_args_len = function.NumTypeParameters();
3429-
ASSERT(parsed_function_->function_type_arguments() != NULL);
3430-
closure += LoadLocal(parsed_function_->function_type_arguments());
3428+
if (target.IsConstructor()) {
3429+
const auto& result_type = AbstractType::Handle(Z, function.result_type());
3430+
ASSERT(result_type.IsFinalized());
3431+
// Instantiate a flattened type arguments vector which
3432+
// includes type arguments corresponding to superclasses.
3433+
// TranslateInstantiatedTypeArguments is smart enough to
3434+
// avoid instantiation and resuse passed function type arguments
3435+
// if there are no extra type arguments in the flattened vector.
3436+
const auto& instantiated_type_arguments =
3437+
TypeArguments::ZoneHandle(Z, result_type.arguments());
3438+
closure +=
3439+
TranslateInstantiatedTypeArguments(instantiated_type_arguments);
3440+
} else {
3441+
type_args_len = function.NumTypeParameters();
3442+
ASSERT(parsed_function_->function_type_arguments() != NULL);
3443+
closure += LoadLocal(parsed_function_->function_type_arguments());
3444+
}
3445+
} else if (target.IsFactory()) {
3446+
// Factories always take an extra implicit argument for
3447+
// type arguments even if their classes don't have type parameters.
3448+
closure += NullConstant();
34313449
}
34323450

34333451
// Push receiver.
3434-
if (!target.is_static()) {
3452+
if (target.IsGenerativeConstructor()) {
3453+
const Class& cls = Class::Handle(Z, target.Owner());
3454+
if (cls.NumTypeArguments() > 0) {
3455+
if (!function.IsGeneric()) {
3456+
Type& cls_type = Type::Handle(Z, cls.DeclarationType());
3457+
closure += Constant(TypeArguments::ZoneHandle(Z, cls_type.arguments()));
3458+
}
3459+
closure += AllocateObject(function.token_pos(), cls, 1);
3460+
} else {
3461+
ASSERT(!function.IsGeneric());
3462+
closure += AllocateObject(function.token_pos(), cls, 0);
3463+
}
3464+
LocalVariable* receiver = MakeTemporary();
3465+
closure += LoadLocal(receiver);
3466+
} else if (!target.is_static()) {
34353467
// The context has a fixed shape: a single variable which is the
34363468
// closed-over receiver.
34373469
closure += LoadLocal(parsed_function_->ParameterVariable(0));
@@ -3445,7 +3477,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
34453477
// Forward parameters to the target.
34463478
intptr_t argument_count = function.NumParameters() -
34473479
function.NumImplicitParameters() +
3448-
(target.is_static() ? 0 : 1);
3480+
target.NumImplicitParameters();
34493481
ASSERT(argument_count == target.NumParameters());
34503482

34513483
Array& argument_names =
@@ -3455,6 +3487,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
34553487
argument_names, ICData::kNoRebind,
34563488
/* result_type = */ NULL, type_args_len);
34573489

3490+
if (target.IsGenerativeConstructor()) {
3491+
// Drop result of constructor invocation, leave receiver
3492+
// instance on the stack.
3493+
closure += Drop();
3494+
}
3495+
34583496
// Return the result.
34593497
closure += Return(function.end_token_pos());
34603498

runtime/vm/compiler/frontend/scope_builder.cc

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,9 +1452,6 @@ void ScopeBuilder::VisitFunctionType(bool simple) {
14521452

14531453
void ScopeBuilder::VisitTypeParameterType() {
14541454
Function& function = Function::Handle(Z, parsed_function_->function().ptr());
1455-
while (function.IsClosureFunction()) {
1456-
function = function.parent_function();
1457-
}
14581455

14591456
helper_.ReadNullability(); // read nullability.
14601457

@@ -1465,19 +1462,25 @@ void ScopeBuilder::VisitTypeParameterType() {
14651462

14661463
intptr_t index = helper_.ReadUInt(); // read index for parameter.
14671464

1468-
if (function.IsFactory()) {
1469-
// The type argument vector is passed as the very first argument to the
1470-
// factory constructor function.
1471-
HandleSpecialLoad(&result_->type_arguments_variable,
1472-
Symbols::TypeArgumentsParameter());
1473-
} else {
1474-
// If the type parameter is a parameter to this or an enclosing function, we
1475-
// can read it directly from the function type arguments vector later.
1476-
// Otherwise, the type arguments vector we need is stored on the instance
1477-
// object, so we need to capture 'this'.
1478-
Class& parent_class = Class::Handle(Z, function.Owner());
1479-
if (index < parent_class.NumTypeParameters()) {
1480-
HandleLoadReceiver();
1465+
if (!function.IsImplicitStaticClosureFunction()) {
1466+
while (function.IsClosureFunction()) {
1467+
function = function.parent_function();
1468+
}
1469+
1470+
if (function.IsFactory()) {
1471+
// The type argument vector is passed as the very first argument to the
1472+
// factory constructor function.
1473+
HandleSpecialLoad(&result_->type_arguments_variable,
1474+
Symbols::TypeArgumentsParameter());
1475+
} else {
1476+
// If the type parameter is a parameter to this or an enclosing function,
1477+
// we can read it directly from the function type arguments vector later.
1478+
// Otherwise, the type arguments vector we need is stored on the instance
1479+
// object, so we need to capture 'this'.
1480+
Class& parent_class = Class::Handle(Z, function.Owner());
1481+
if (index < parent_class.NumTypeParameters()) {
1482+
HandleLoadReceiver();
1483+
}
14811484
}
14821485
}
14831486

runtime/vm/object.cc

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7519,7 +7519,7 @@ FunctionPtr Function::GetOutermostFunction() const {
75197519
}
75207520

75217521
FunctionPtr Function::implicit_closure_function() const {
7522-
if (IsClosureFunction() || IsFactory() || IsDispatcherOrImplicitAccessor() ||
7522+
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
75237523
IsFieldInitializer() || IsFfiTrampoline()) {
75247524
return Function::null();
75257525
}
@@ -7689,6 +7689,7 @@ void Function::SetForwardingChecks(const Array& checks) const {
76897689
// native function: Array[0] = String native name
76907690
// Array[1] = Function implicit closure function
76917691
// regular function: Function for implicit closure function
7692+
// constructor, factory: Function for implicit closure function
76927693
// ffi trampoline function: FfiTrampolineData (Dart->C)
76937694
// dyn inv forwarder: Array[0] = Function target
76947695
// Array[1] = TypeArguments default type args
@@ -9160,6 +9161,7 @@ FunctionPtr Function::New(const FunctionType& signature,
91609161
FunctionPtr Function::NewClosureFunctionWithKind(UntaggedFunction::Kind kind,
91619162
const String& name,
91629163
const Function& parent,
9164+
bool is_static,
91639165
TokenPosition token_pos,
91649166
const Object& owner) {
91659167
ASSERT((kind == UntaggedFunction::kClosureFunction) ||
@@ -9171,7 +9173,7 @@ FunctionPtr Function::NewClosureFunctionWithKind(UntaggedFunction::Kind kind,
91719173
: 0));
91729174
const Function& result = Function::Handle(
91739175
Function::New(signature, name, kind,
9174-
/* is_static = */ parent.is_static(),
9176+
/* is_static = */ is_static,
91759177
/* is_const = */ false,
91769178
/* is_abstract = */ false,
91779179
/* is_external = */ false,
@@ -9186,16 +9188,18 @@ FunctionPtr Function::NewClosureFunction(const String& name,
91869188
// Use the owner defining the parent function and not the class containing it.
91879189
const Object& parent_owner = Object::Handle(parent.RawOwner());
91889190
return NewClosureFunctionWithKind(UntaggedFunction::kClosureFunction, name,
9189-
parent, token_pos, parent_owner);
9191+
parent, parent.is_static(), token_pos,
9192+
parent_owner);
91909193
}
91919194

91929195
FunctionPtr Function::NewImplicitClosureFunction(const String& name,
91939196
const Function& parent,
91949197
TokenPosition token_pos) {
91959198
// Use the owner defining the parent function and not the class containing it.
91969199
const Object& parent_owner = Object::Handle(parent.RawOwner());
9197-
return NewClosureFunctionWithKind(UntaggedFunction::kImplicitClosureFunction,
9198-
name, parent, token_pos, parent_owner);
9200+
return NewClosureFunctionWithKind(
9201+
UntaggedFunction::kImplicitClosureFunction, name, parent,
9202+
parent.is_static() || parent.IsConstructor(), token_pos, parent_owner);
91999203
}
92009204

92019205
bool Function::SafeToClosurize() const {
@@ -9241,7 +9245,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
92419245
zone, NewImplicitClosureFunction(closure_name, *this, token_pos()));
92429246

92439247
// Set closure function's context scope.
9244-
if (is_static()) {
9248+
if (is_static() || IsConstructor()) {
92459249
closure_function.set_context_scope(Object::empty_context_scope());
92469250
} else {
92479251
const ContextScope& context_scope = ContextScope::Handle(
@@ -9252,15 +9256,46 @@ FunctionPtr Function::ImplicitClosureFunction() const {
92529256
FunctionType& closure_signature =
92539257
FunctionType::Handle(zone, closure_function.signature());
92549258

9255-
// Set closure function's type parameters.
9256-
// This function cannot be local, therefore it has no generic parent.
9257-
// Its implicit closure function therefore has no generic parent function
9258-
// either. That is why it is safe to simply copy the type parameters.
9259-
closure_signature.SetTypeParameters(
9260-
TypeParameters::Handle(zone, type_parameters()));
9259+
// Set closure function's type parameters and result type.
9260+
if (IsConstructor()) {
9261+
// Inherit type parameters from owner class.
9262+
const auto& cls = Class::Handle(zone, Owner());
9263+
closure_signature.SetTypeParameters(
9264+
TypeParameters::Handle(zone, cls.type_parameters()));
9265+
ASSERT(closure_signature.NumTypeParameters() == cls.NumTypeParameters());
9266+
9267+
Type& result_type = Type::Handle(zone);
9268+
const Nullability result_nullability =
9269+
(nnbd_mode() == NNBDMode::kOptedInLib) ? Nullability::kNonNullable
9270+
: Nullability::kLegacy;
9271+
if (cls.IsGeneric()) {
9272+
TypeArguments& type_args = TypeArguments::Handle(zone);
9273+
const intptr_t num_type_params = cls.NumTypeParameters();
9274+
ASSERT(num_type_params > 0);
9275+
type_args = TypeArguments::New(num_type_params);
9276+
TypeParameter& type_param = TypeParameter::Handle(zone);
9277+
for (intptr_t i = 0; i < num_type_params; i++) {
9278+
type_param = closure_signature.TypeParameterAt(i);
9279+
type_args.SetTypeAt(i, type_param);
9280+
}
9281+
result_type = Type::New(cls, type_args, result_nullability);
9282+
result_type ^= ClassFinalizer::FinalizeType(result_type);
9283+
} else {
9284+
result_type = cls.DeclarationType();
9285+
result_type = result_type.ToNullability(result_nullability, Heap::kOld);
9286+
}
9287+
closure_signature.set_result_type(result_type);
9288+
} else {
9289+
// This function cannot be local, therefore it has no generic parent.
9290+
// Its implicit closure function therefore has no generic parent function
9291+
// either. That is why it is safe to simply copy the type parameters.
9292+
closure_signature.SetTypeParameters(
9293+
TypeParameters::Handle(zone, type_parameters()));
92619294

9262-
// Set closure function's result type to this result type.
9263-
closure_signature.set_result_type(AbstractType::Handle(zone, result_type()));
9295+
// Set closure function's result type to this result type.
9296+
closure_signature.set_result_type(
9297+
AbstractType::Handle(zone, result_type()));
9298+
}
92649299

92659300
// Set closure function's end token to this end token.
92669301
closure_function.set_end_token_pos(end_token_pos());
@@ -9274,8 +9309,9 @@ FunctionPtr Function::ImplicitClosureFunction() const {
92749309
// removing the receiver if this is an instance method and adding the closure
92759310
// object as first parameter.
92769311
const int kClosure = 1;
9277-
const int has_receiver = is_static() ? 0 : 1;
9278-
const int num_fixed_params = kClosure - has_receiver + num_fixed_parameters();
9312+
const int num_implicit_params = NumImplicitParameters();
9313+
const int num_fixed_params =
9314+
kClosure - num_implicit_params + num_fixed_parameters();
92799315
const int num_opt_params = NumOptionalParameters();
92809316
const bool has_opt_pos_params = HasOptionalPositionalParameters();
92819317
const int num_params = num_fixed_params + num_opt_params;
@@ -9294,19 +9330,19 @@ FunctionPtr Function::ImplicitClosureFunction() const {
92949330
closure_signature.SetParameterTypeAt(0, param_type);
92959331
closure_function.SetParameterNameAt(0, Symbols::ClosureParameter());
92969332
for (int i = kClosure; i < num_pos_params; i++) {
9297-
param_type = ParameterTypeAt(has_receiver - kClosure + i);
9333+
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
92989334
closure_signature.SetParameterTypeAt(i, param_type);
9299-
param_name = ParameterNameAt(has_receiver - kClosure + i);
9335+
param_name = ParameterNameAt(num_implicit_params - kClosure + i);
93009336
// Set the name in the function for positional parameters.
93019337
closure_function.SetParameterNameAt(i, param_name);
93029338
}
93039339
for (int i = num_pos_params; i < num_params; i++) {
9304-
param_type = ParameterTypeAt(has_receiver - kClosure + i);
9340+
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
93059341
closure_signature.SetParameterTypeAt(i, param_type);
9306-
param_name = ParameterNameAt(has_receiver - kClosure + i);
9342+
param_name = ParameterNameAt(num_implicit_params - kClosure + i);
93079343
// Set the name in the signature for named parameters.
93089344
closure_signature.SetParameterNameAt(i, param_name);
9309-
if (IsRequiredAt(has_receiver - kClosure + i)) {
9345+
if (IsRequiredAt(num_implicit_params - kClosure + i)) {
93109346
closure_signature.SetIsRequiredAt(i);
93119347
}
93129348
}
@@ -9315,7 +9351,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
93159351

93169352
// Change covariant parameter types to either Object? for an opted-in implicit
93179353
// closure or to Object* for a legacy implicit closure.
9318-
if (!is_static()) {
9354+
if (!is_static() && !IsConstructor()) {
93199355
BitVector is_covariant(zone, NumParameters());
93209356
BitVector is_generic_covariant_impl(zone, NumParameters());
93219357
kernel::ReadParameterCovariance(*this, &is_covariant,
@@ -9328,7 +9364,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
93289364
: object_store->legacy_object_type();
93299365
ASSERT(object_type.IsCanonical());
93309366
for (intptr_t i = kClosure; i < num_params; ++i) {
9331-
const intptr_t original_param_index = has_receiver - kClosure + i;
9367+
const intptr_t original_param_index = num_implicit_params - kClosure + i;
93329368
if (is_covariant.Contains(original_param_index) ||
93339369
is_generic_covariant_impl.Contains(original_param_index)) {
93349370
closure_signature.SetParameterTypeAt(i, object_type);
@@ -9340,6 +9376,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
93409376
closure_function.SetSignature(closure_signature);
93419377
set_implicit_closure_function(closure_function);
93429378
ASSERT(closure_function.IsImplicitClosureFunction());
9379+
ASSERT(HasImplicitClosureFunction());
93439380
return closure_function.ptr();
93449381
#endif // defined(DART_PRECOMPILED_RUNTIME)
93459382
}

0 commit comments

Comments
 (0)