[vm] Update number of parent function type arguments

Number of parent type arguments in function types is critical
for instantiation and subtype checks when there are nested
generic function types. So it is important to update
number of parent type arguments when parent function type is updated.

This change adds mechanism for updating number of parent
type arguments and uses the new mechanism in 2 cases:

1) When a type parameter is substituted during type instantiation.
   The substituted type can have nested generic function types and
   it should be updated if substituted inside another generic
   function type.

2) When creating a constructor tear-off for a generic class.
   Parameter types of the constructor could have nested generic
   function types and they should be updated as tear-off itself is a
   generic function.

TEST=language/function_subtype/generic_function_type_substitution_test
TEST=language/regress/regress50905_test

Fixes https://github.com/dart-lang/sdk/issues/50614
Fixes https://github.com/dart-lang/sdk/issues/50905

Change-Id: If13baa420939f5ac002ce32f3909953fb6998bd2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278525
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2023-01-10 03:10:14 +00:00 committed by Commit Queue
parent 3157f9ff8d
commit 88400bdc42
3 changed files with 400 additions and 21 deletions

View file

@ -7289,7 +7289,8 @@ TypeArgumentsPtr TypeArguments::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
ASSERT(!IsInstantiated());
if ((instantiator_type_arguments.IsNull() ||
instantiator_type_arguments.Length() == Length()) &&
@ -7311,7 +7312,8 @@ TypeArgumentsPtr TypeArguments::InstantiateFrom(
if (!type.IsNull() && !type.IsInstantiated()) {
type = type.InstantiateFrom(instantiator_type_arguments,
function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
// A returned null type indicates a failed instantiation in dead code that
// must be propagated up to the caller, the optimizing compiler.
if (type.IsNull()) {
@ -7323,6 +7325,37 @@ TypeArgumentsPtr TypeArguments::InstantiateFrom(
return instantiated_array.ptr();
}
TypeArgumentsPtr TypeArguments::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
Zone* zone = Thread::Current()->zone();
TypeArguments* updated_args = nullptr;
AbstractType& type = AbstractType::Handle(zone);
AbstractType& updated = AbstractType::Handle(zone);
for (intptr_t i = 0, n = Length(); i < n; ++i) {
type = TypeAt(i);
updated =
type.UpdateParentFunctionType(num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail);
if (type.ptr() != updated.ptr()) {
if (updated_args == nullptr) {
updated_args =
&TypeArguments::Handle(zone, TypeArguments::New(n, space));
for (intptr_t j = 0; j < i; ++j) {
type = TypeAt(j);
updated_args->SetTypeAt(j, type);
}
}
}
if (updated_args != nullptr) {
updated_args->SetTypeAt(i, updated);
}
}
return (updated_args != nullptr) ? updated_args->ptr() : ptr();
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// A local flag used only in object_test.cc that, when true, causes a failure
// when a cache entry for the given instantiator and function type arguments
@ -9269,7 +9302,8 @@ AbstractTypePtr FunctionType::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
ASSERT(IsFinalized() || IsBeingFinalized());
Zone* zone = Thread::Current()->zone();
const intptr_t num_parent_type_args = NumParentTypeArguments();
@ -9293,6 +9327,12 @@ AbstractTypePtr FunctionType::InstantiateFrom(
num_free_fun_type_params < num_parent_type_args
? num_parent_type_args - num_free_fun_type_params
: 0;
// Adjust number of parent type arguments for all nested substituted types.
num_parent_type_args_adjustment =
remaining_parent_type_params +
(delete_type_parameters ? 0 : NumTypeParameters());
FunctionType& sig = FunctionType::Handle(
FunctionType::New(remaining_parent_type_params, nullability(), space));
AbstractType& type = AbstractType::Handle(zone);
@ -9313,14 +9353,16 @@ AbstractTypePtr FunctionType::InstantiateFrom(
if (!type_args.IsNull() && !type_args.IsInstantiated()) {
type_args = type_args.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
}
sig_type_params.set_bounds(type_args);
type_args = type_params.defaults();
if (!type_args.IsNull() && !type_args.IsInstantiated()) {
type_args = type_args.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
}
sig_type_params.set_defaults(type_args);
sig.SetTypeParameters(sig_type_params);
@ -9329,9 +9371,10 @@ AbstractTypePtr FunctionType::InstantiateFrom(
type = result_type();
if (!type.IsInstantiated()) {
type = type.InstantiateFrom(instantiator_type_arguments,
function_type_arguments,
num_free_fun_type_params, space, trail);
type =
type.InstantiateFrom(instantiator_type_arguments,
function_type_arguments, num_free_fun_type_params,
space, trail, num_parent_type_args_adjustment);
// A returned null type indicates a failed instantiation in dead code that
// must be propagated up to the caller, the optimizing compiler.
if (type.IsNull()) {
@ -9350,7 +9393,8 @@ AbstractTypePtr FunctionType::InstantiateFrom(
if (!type.IsInstantiated()) {
type = type.InstantiateFrom(instantiator_type_arguments,
function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
// A returned null type indicates a failed instantiation in dead code that
// must be propagated up to the caller, the optimizing compiler.
if (type.IsNull()) {
@ -9377,6 +9421,80 @@ AbstractTypePtr FunctionType::InstantiateFrom(
return sig.ptr();
}
AbstractTypePtr FunctionType::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
ASSERT(num_parent_type_args_adjustment > 0);
ASSERT(IsFinalized());
Zone* zone = Thread::Current()->zone();
const intptr_t old_num_parent_type_args = NumParentTypeArguments();
// From now on, adjust all type parameter types
// which belong to this or nested function types.
if (num_free_fun_type_params > old_num_parent_type_args) {
num_free_fun_type_params = old_num_parent_type_args;
}
FunctionType& new_type = FunctionType::Handle(
zone, FunctionType::New(
NumParentTypeArguments() + num_parent_type_args_adjustment,
nullability(), space));
AbstractType& type = AbstractType::Handle(zone);
const TypeParameters& type_params =
TypeParameters::Handle(zone, type_parameters());
if (!type_params.IsNull()) {
const TypeParameters& new_type_params =
TypeParameters::Handle(zone, TypeParameters::New());
// No need to set names that are ignored in a signature, however, the
// length of the names array defines the number of type parameters.
new_type_params.set_names(Array::Handle(zone, type_params.names()));
new_type_params.set_flags(Array::Handle(zone, type_params.flags()));
TypeArguments& type_args = TypeArguments::Handle(zone);
type_args = type_params.bounds();
if (!type_args.IsNull()) {
type_args = type_args.UpdateParentFunctionType(
num_parent_type_args_adjustment, num_free_fun_type_params, space,
trail);
}
new_type_params.set_bounds(type_args);
type_args = type_params.defaults();
if (!type_args.IsNull()) {
type_args = type_args.UpdateParentFunctionType(
num_parent_type_args_adjustment, num_free_fun_type_params, space,
trail);
}
new_type_params.set_defaults(type_args);
new_type.SetTypeParameters(new_type_params);
}
type = result_type();
type = type.UpdateParentFunctionType(num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail);
new_type.set_result_type(type);
const intptr_t num_params = NumParameters();
new_type.set_num_implicit_parameters(num_implicit_parameters());
new_type.set_num_fixed_parameters(num_fixed_parameters());
new_type.SetNumOptionalParameters(NumOptionalParameters(),
HasOptionalPositionalParameters());
new_type.set_parameter_types(Array::Handle(Array::New(num_params, space)));
for (intptr_t i = 0; i < num_params; i++) {
type = ParameterTypeAt(i);
type =
type.UpdateParentFunctionType(num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail);
new_type.SetParameterTypeAt(i, type);
}
new_type.set_named_parameter_names(
Array::Handle(zone, named_parameter_names()));
new_type.SetIsFinalized();
return new_type.ptr();
}
// Checks if the type of the specified parameter of this signature is a
// supertype of the type of the specified parameter of the other signature
// (i.e. check parameter contravariance).
@ -9880,13 +9998,16 @@ FunctionPtr Function::ImplicitClosureFunction() const {
const auto& instantiator_type_args = TypeArguments::Handle(
zone, AbstractType::Handle(zone, closure_signature.result_type())
.arguments());
const intptr_t num_type_args = closure_signature.NumTypeArguments();
auto& param_type = AbstractType::Handle(zone);
for (intptr_t i = kClosure; i < num_params; ++i) {
param_type = closure_signature.ParameterTypeAt(i);
param_type = param_type.UpdateParentFunctionType(num_type_args, kAllFree,
Heap::kOld);
if (!param_type.IsInstantiated()) {
param_type = param_type.InstantiateFrom(instantiator_type_args,
Object::null_type_arguments(),
kAllFree, Heap::kOld);
param_type = param_type.InstantiateFrom(
instantiator_type_args, Object::null_type_arguments(),
kNoneFree /* avoid truncating parent type args */, Heap::kOld);
closure_signature.SetParameterTypeAt(i, param_type);
}
}
@ -20701,7 +20822,8 @@ AbstractTypePtr AbstractType::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
// All subclasses should implement this appropriately, so the only value that
// should reach this implementation should be the null value.
ASSERT(IsNull());
@ -20710,6 +20832,15 @@ AbstractTypePtr AbstractType::InstantiateFrom(
return NULL;
}
AbstractTypePtr AbstractType::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
UNREACHABLE();
return NULL;
}
AbstractTypePtr AbstractType::Canonicalize(Thread* thread,
TrailPtr trail) const {
// All subclasses should implement this appropriately, so the only value that
@ -21522,7 +21653,8 @@ AbstractTypePtr Type::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
Zone* zone = Thread::Current()->zone();
ASSERT(IsFinalized() || IsBeingFinalized());
ASSERT(!IsInstantiated());
@ -21534,7 +21666,7 @@ AbstractTypePtr Type::InstantiateFrom(
ASSERT(type_arguments.Length() == cls.NumTypeArguments());
type_arguments = type_arguments.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail, num_parent_type_args_adjustment);
// A returned empty_type_arguments indicates a failed instantiation in dead
// code that must be propagated up to the caller, the optimizing compiler.
if (type_arguments.ptr() == Object::empty_type_arguments().ptr()) {
@ -21555,6 +21687,32 @@ AbstractTypePtr Type::InstantiateFrom(
return instantiated_type.NormalizeFutureOrType(space);
}
AbstractTypePtr Type::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
ASSERT(IsFinalized());
ASSERT(num_parent_type_args_adjustment > 0);
if (arguments() == Object::null()) {
return ptr();
}
Zone* zone = Thread::Current()->zone();
const auto& type_args = TypeArguments::Handle(zone, arguments());
const auto& updated_type_args =
TypeArguments::Handle(zone, type_args.UpdateParentFunctionType(
num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail));
if (type_args.ptr() == updated_type_args.ptr()) {
return ptr();
}
const Class& cls = Class::Handle(zone, type_class());
const Type& new_type = Type::Handle(
zone, Type::New(cls, updated_type_args, nullability(), space));
new_type.SetIsFinalized();
return new_type.ptr();
}
// Certain built-in classes are treated as syntactically equivalent.
static classid_t NormalizeClassIdForSyntacticalTypeEquality(classid_t cid) {
if (IsIntegerClassId(cid)) {
@ -22411,7 +22569,8 @@ AbstractTypePtr TypeRef::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
TypeRef& instantiated_type_ref = TypeRef::Handle();
instantiated_type_ref ^= OnlyBuddyInTrail(trail);
if (!instantiated_type_ref.IsNull()) {
@ -22425,7 +22584,7 @@ AbstractTypePtr TypeRef::InstantiateFrom(
AbstractType& instantiated_ref_type = AbstractType::Handle();
instantiated_ref_type = ref_type.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail, num_parent_type_args_adjustment);
// A returned null type indicates a failed instantiation in dead code that
// must be propagated up to the caller, the optimizing compiler.
if (instantiated_ref_type.IsNull()) {
@ -22439,6 +22598,35 @@ AbstractTypePtr TypeRef::InstantiateFrom(
return instantiated_type_ref.ptr();
}
AbstractTypePtr TypeRef::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
ASSERT(IsFinalized());
ASSERT(num_parent_type_args_adjustment > 0);
Zone* zone = Thread::Current()->zone();
TypeRef& new_type_ref = TypeRef::Handle(zone);
new_type_ref ^= OnlyBuddyInTrail(trail);
if (!new_type_ref.IsNull()) {
return new_type_ref.ptr();
}
new_type_ref = TypeRef::New();
AddOnlyBuddyToTrail(&trail, new_type_ref);
AbstractType& ref_type = AbstractType::Handle(type());
ASSERT(!ref_type.IsNull() && !ref_type.IsTypeRef());
const auto& updated_ref_type =
AbstractType::Handle(zone, ref_type.UpdateParentFunctionType(
num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail));
ASSERT(!updated_ref_type.IsTypeRef());
new_type_ref.set_type(updated_ref_type);
return new_type_ref.ptr();
}
void TypeRef::set_type(const AbstractType& value) const {
ASSERT(!value.IsTypeRef());
if (value.IsNull()) {
@ -22743,8 +22931,10 @@ AbstractTypePtr TypeParameter::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
AbstractType& result = AbstractType::Handle();
bool substituted = false;
if (IsFunctionTypeParameter()) {
ASSERT(IsFinalized());
if (index() >= num_free_fun_type_params) {
@ -22755,7 +22945,8 @@ AbstractTypePtr TypeParameter::InstantiateFrom(
if (!upper_bound.IsInstantiated()) {
upper_bound = upper_bound.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
}
if ((upper_bound.IsTypeRef() &&
TypeRef::Cast(upper_bound).type() == Type::NeverType()) ||
@ -22774,6 +22965,7 @@ AbstractTypePtr TypeParameter::InstantiateFrom(
return Type::DynamicType();
} else {
result = function_type_arguments.TypeAt(index());
substituted = true;
ASSERT(!result.IsTypeParameter());
}
} else {
@ -22792,15 +22984,49 @@ AbstractTypePtr TypeParameter::InstantiateFrom(
return AbstractType::null();
}
result = instantiator_type_arguments.TypeAt(index());
substituted = true;
// Instantiating a class type parameter cannot result in a
// function type parameter.
// Bounds of class type parameters are ignored in the VM.
}
result = result.SetInstantiatedNullability(*this, space);
if (substituted && (num_parent_type_args_adjustment != 0)) {
// This type parameter is used inside a generic function type.
// A type being substituted can have nested function types,
// whose number of parent function type arguments should be adjusted
// after the substitution.
result = result.UpdateParentFunctionType(num_parent_type_args_adjustment,
kAllFree, space);
}
// Canonicalization is not part of instantiation.
return result.NormalizeFutureOrType(space);
}
AbstractTypePtr TypeParameter::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
ASSERT(IsFinalized());
ASSERT(num_parent_type_args_adjustment > 0);
if (IsFunctionTypeParameter() && (index() >= num_free_fun_type_params)) {
Zone* zone = Thread::Current()->zone();
auto& new_tp = TypeParameter::Handle(zone);
new_tp ^= Object::Clone(*this, space);
new_tp.set_base(base() + num_parent_type_args_adjustment);
new_tp.set_index(index() + num_parent_type_args_adjustment);
auto& type = AbstractType::Handle(zone, bound());
type =
type.UpdateParentFunctionType(num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail);
new_tp.set_bound(type);
ASSERT(new_tp.IsFinalized());
return new_tp.ptr();
} else {
return ptr();
}
}
AbstractTypePtr TypeParameter::Canonicalize(Thread* thread,
TrailPtr trail) const {
ASSERT(IsFinalized());
@ -27904,7 +28130,8 @@ AbstractTypePtr RecordType::InstantiateFrom(
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
TrailPtr trail,
intptr_t num_parent_type_args_adjustment) const {
ASSERT(IsFinalized() || IsBeingFinalized());
Zone* zone = Thread::Current()->zone();
@ -27918,7 +28145,8 @@ AbstractTypePtr RecordType::InstantiateFrom(
if (!type.IsInstantiated()) {
type = type.InstantiateFrom(instantiator_type_arguments,
function_type_arguments,
num_free_fun_type_params, space, trail);
num_free_fun_type_params, space, trail,
num_parent_type_args_adjustment);
// A returned null type indicates a failed instantiation in dead code that
// must be propagated up to the caller, the optimizing compiler.
if (type.IsNull()) {
@ -27943,6 +28171,45 @@ AbstractTypePtr RecordType::InstantiateFrom(
return rec.ptr();
}
AbstractTypePtr RecordType::UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail) const {
ASSERT(IsFinalized());
ASSERT(num_parent_type_args_adjustment > 0);
Zone* zone = Thread::Current()->zone();
const auto& types = Array::Handle(zone, field_types());
Array* updated_types = nullptr;
auto& type = AbstractType::Handle(zone);
auto& updated = AbstractType::Handle(zone);
for (intptr_t i = 0, n = NumFields(); i < n; ++i) {
type ^= types.At(i);
updated =
type.UpdateParentFunctionType(num_parent_type_args_adjustment,
num_free_fun_type_params, space, trail);
if (type.ptr() != updated.ptr()) {
if (updated_types == nullptr) {
updated_types = &Array::Handle(zone, Array::New(n, space));
for (intptr_t j = 0; j < i; ++j) {
type ^= types.At(j);
updated_types->SetAt(j, type);
}
}
}
if (updated_types != nullptr) {
updated_types->SetAt(i, updated);
}
}
if (updated_types == nullptr) {
return ptr();
}
const auto& new_rt = RecordType::Handle(
zone, RecordType::New(shape(), *updated_types, nullability(), space));
new_rt.SetIsFinalized();
return new_rt.ptr();
}
bool RecordType::IsSubtypeOf(const RecordType& other, Heap::Space space) const {
if (ptr() == other.ptr()) {
return true;

View file

@ -8118,6 +8118,15 @@ class TypeArguments : public Instance {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
// Update number of parent function type arguments for
// all elements of this vector.
TypeArgumentsPtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
// Runtime instantiation with canonicalization. Not to be used during type
@ -8465,6 +8474,21 @@ class AbstractType : public Instance {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
// Update number of parent function type arguments for the
// nested function types and their type parameters.
//
// This adjustment is needed when nesting one generic function type
// inside another.
// Number of parent function type arguments is adjusted by
// [num_parent_type_args_adjustment].
// Type parameters up to [num_free_fun_type_params] are not adjusted.
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
// Caller must hold IsolateGroup::constant_canonicalization_mutex_.
@ -8754,7 +8778,15 @@ class Type : public AbstractType {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
virtual AbstractTypePtr Canonicalize(Thread* thread, TrailPtr trail) const;
#if defined(DEBUG)
// Check if type is canonical.
@ -8894,7 +8926,15 @@ class FunctionType : public AbstractType {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
virtual AbstractTypePtr Canonicalize(Thread* thread, TrailPtr trail) const;
#if defined(DEBUG)
// Check if type is canonical.
@ -9173,7 +9213,15 @@ class TypeRef : public AbstractType {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
virtual AbstractTypePtr Canonicalize(Thread* thread, TrailPtr trail) const;
#if defined(DEBUG)
// Check if typeref is canonical.
@ -9258,7 +9306,15 @@ class TypeParameter : public AbstractType {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
virtual AbstractTypePtr Canonicalize(Thread* thread, TrailPtr trail) const;
#if defined(DEBUG)
// Check if type parameter is canonical.
@ -10964,7 +11020,15 @@ class RecordType : public AbstractType {
const TypeArguments& function_type_arguments,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr,
intptr_t num_parent_type_args_adjustment = 0) const;
virtual AbstractTypePtr UpdateParentFunctionType(
intptr_t num_parent_type_args_adjustment,
intptr_t num_free_fun_type_params,
Heap::Space space,
TrailPtr trail = nullptr) const;
virtual AbstractTypePtr Canonicalize(Thread* thread, TrailPtr trail) const;
#if defined(DEBUG)
// Check if type is canonical.

View file

@ -0,0 +1,48 @@
// Copyright (c) 2023, 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.
// Verifies that constructor tear-off of a generic class
// has a correct type and can be called via Function.apply.
//
// Regression test for https://github.com/dart-lang/sdk/issues/50905.
import 'package:expect/expect.dart';
class A<T> {
A({required T Function() x}) {
Expect.equals(f1, x);
}
}
class B<T> {
B({required Map<S, T> Function<S>() x}) {
Expect.equals(f2, x);
}
}
int f1() => 0;
Map<U, int> f2<U>() => {};
A<V> t1<V>({required V Function() x}) => throw 'unused';
A<int> t2({required int Function() x}) => throw 'unused';
B<V> t3<V>({required Map<U, V> Function<U>() x}) => throw 'unused';
B<int> t4({required Map<U, int> Function<U>() x}) => throw 'unused';
void main() {
Function c1 = A.new;
Expect.equals(t1.runtimeType.toString(), c1.runtimeType.toString());
Function c2 = A<int>.new;
Expect.equals(t2.runtimeType.toString(), c2.runtimeType.toString());
final o2 = Function.apply(c2, [], {#x: f1});
Expect.isTrue(o2 is A<int>);
Function c3 = B.new;
Expect.equals(t3.runtimeType.toString(), c3.runtimeType.toString());
Function c4 = B<int>.new;
Expect.equals(t4.runtimeType.toString(), c4.runtimeType.toString());
final o4 = Function.apply(c4, [], {#x: f2});
Expect.isTrue(o4 is B<int>);
}