[VM/runtime] Insert TypeRef when type parameter bound refers to type parameters.

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

TEST=added regression test

Change-Id: Iecdccd4af02c374d4d17d0c0d7985fdaeec5e42a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/202820
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
This commit is contained in:
Regis Crelier 2021-06-16 18:25:37 +00:00 committed by commit-bot@chromium.org
parent f00c3ded30
commit bae9560c8a
7 changed files with 71 additions and 28 deletions

View file

@ -732,15 +732,19 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
return type.ptr();
}
if (type.IsTypeRef()) {
// The referenced type will be finalized later by the code that set the
// is_being_finalized mark bit.
return type.ptr();
}
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
if (type.IsTypeParameter() && type.IsBeingFinalized()) {
// The base and index have already been adjusted, but the bound referring
// back to the type parameter is still being finalized.
if (type.IsTypeRef()) {
if (type.IsBeingFinalized()) {
// The referenced type will be finalized later by the code that set the
// is_being_finalized mark bit.
return type.ptr();
}
AbstractType& ref_type =
AbstractType::Handle(zone, TypeRef::Cast(type).type());
ref_type = FinalizeType(ref_type, finalization, pending_types);
TypeRef::Cast(type).set_type(ref_type);
return type.ptr();
}
@ -751,9 +755,6 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
// Mark the type as being finalized in order to detect self reference.
type.SetIsBeingFinalized();
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
if (FLAG_trace_type_finalization) {
THR_Print("Finalizing type '%s'\n",
String::Handle(zone, type.Name()).ToCString());
@ -780,21 +781,13 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
type_parameter.set_parameterized_class_id(kClassCid);
}
type_parameter.SetIsFinalized();
AbstractType& upper_bound = AbstractType::Handle(zone);
upper_bound = type_parameter.bound();
if (upper_bound.IsBeingFinalized()) {
if (upper_bound.IsTypeRef()) {
// Nothing to do.
} else {
upper_bound = TypeRef::New(upper_bound);
type_parameter.set_bound(upper_bound);
upper_bound = FinalizeType(upper_bound, kFinalize);
}
} else {
if (!upper_bound.IsBeingFinalized()) {
upper_bound = FinalizeType(upper_bound, kFinalize);
type_parameter.set_bound(upper_bound);
}
type_parameter.SetIsFinalized();
if (FLAG_trace_type_finalization) {
THR_Print("Done finalizing type parameter at index %" Pd "\n",

View file

@ -917,7 +917,8 @@ static bool CanPotentiallyBeSmi(const AbstractType& type, bool recurse) {
// Comparable<int>).
if (type.IsFutureOrType() ||
type.type_class() == CompilerState::Current().ComparableClass().ptr()) {
const auto& args = TypeArguments::Handle(Type::Cast(type).arguments());
// Type may be a TypeRef.
const auto& args = TypeArguments::Handle(type.arguments());
const auto& arg0 = AbstractType::Handle(args.TypeAt(0));
return !recurse || CanPotentiallyBeSmi(arg0, /*recurse=*/true);
}

View file

@ -1815,6 +1815,9 @@ void FlowGraphBuilder::BuildTypeArgumentTypeChecks(TypeChecksToBuild mode,
type_param = dart_function.TypeParameterAt(i);
}
ASSERT(type_param.IsFinalized());
if (bound.IsTypeRef()) {
bound = TypeRef::Cast(bound).type();
}
check_bounds +=
AssertSubtype(TokenPosition::kNoSource, type_param, bound, name);
}
@ -4507,6 +4510,10 @@ bool FlowGraphBuilder::NeedsNullAssertion(const AbstractType& type) {
if (!type.IsNonNullable()) {
return false;
}
if (type.IsTypeRef()) {
return NeedsNullAssertion(
AbstractType::Handle(Z, TypeRef::Cast(type).type()));
}
if (type.IsTypeParameter()) {
return NeedsNullAssertion(
AbstractType::Handle(Z, TypeParameter::Cast(type).bound()));

View file

@ -3038,6 +3038,7 @@ TypeTranslator::TypeTranslator(KernelReaderHelper* helper,
zone_(translation_helper_.zone()),
result_(AbstractType::Handle(translation_helper_.zone())),
finalize_(finalize),
refers_to_derived_type_param_(false),
apply_canonical_type_erasure_(apply_canonical_type_erasure),
in_constant_context_(in_constant_context) {}
@ -3094,6 +3095,10 @@ void TypeTranslator::BuildTypeInternal() {
break;
case kTypeParameterType:
BuildTypeParameterType();
if (result_.IsTypeParameter() &&
TypeParameter::Cast(result_).bound() == AbstractType::null()) {
refers_to_derived_type_param_ = true;
}
break;
default:
helper_->ReportUnexpectedTag("type", tag);
@ -3475,8 +3480,14 @@ void TypeTranslator::LoadAndSetupBounds(
TypeParameterHelper helper(helper_);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
bool saved_refers_to_derived_type_param = refers_to_derived_type_param_;
refers_to_derived_type_param_ = false;
AbstractType& bound = BuildTypeWithoutFinalization(); // read ith bound.
ASSERT(!bound.IsNull());
if (refers_to_derived_type_param_) {
bound = TypeRef::New(bound);
}
refers_to_derived_type_param_ = saved_refers_to_derived_type_param;
type_parameters.SetBoundAt(i, bound);
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
AbstractType& default_arg = BuildTypeWithoutFinalization();
@ -3503,6 +3514,7 @@ void TypeTranslator::LoadAndSetupBounds(
derived.index() >= offset &&
derived.index() < offset + type_parameter_count))) {
bound = type_parameters.BoundAt(derived.index() - offset);
ASSERT(!bound.IsNull());
derived.set_bound(bound);
}
}

View file

@ -1538,6 +1538,7 @@ class TypeTranslator {
Zone* zone_;
AbstractType& result_;
bool finalize_;
bool refers_to_derived_type_param_;
const bool apply_canonical_type_erasure_;
const bool in_constant_context_;

View file

@ -19562,6 +19562,10 @@ bool AbstractType::IsStrictlyNonNullable() const {
return false;
}
if (IsTypeRef()) {
return AbstractType::Handle(zone, TypeRef::Cast(*this).type())
.IsStrictlyNonNullable();
}
if (IsTypeParameter()) {
const auto& bound =
AbstractType::Handle(zone, TypeParameter::Cast(*this).bound());
@ -19911,10 +19915,12 @@ void AbstractType::PrintName(NameVisibility name_visibility,
const TypeArguments& args = TypeArguments::Handle(zone, arguments());
const intptr_t num_args = args.IsNull() ? 0 : args.Length();
intptr_t first_type_param_index;
intptr_t num_type_params; // Number of type parameters to print.
intptr_t num_type_params = num_args; // Number of type parameters to print.
cls = type_class();
// Do not print the full vector, but only the declared type parameters.
num_type_params = cls.NumTypeParameters();
if (cls.is_declaration_loaded()) {
// Do not print the full vector, but only the declared type parameters.
num_type_params = cls.NumTypeParameters();
}
printer->AddString(cls.NameCString(name_visibility));
if (num_type_params > num_args) {
first_type_param_index = 0;
@ -20055,6 +20061,7 @@ bool AbstractType::IsFfiPointerType() const {
}
AbstractTypePtr AbstractType::UnwrapFutureOr() const {
// Works properly for a TypeRef without dereferencing it.
if (!IsFutureOrType()) {
return ptr();
}
@ -20982,8 +20989,13 @@ uword FunctionType::ComputeHash() const {
if (num_type_params > 0) {
const TypeParameters& type_params =
TypeParameters::Handle(type_parameters());
const TypeArguments& bounds = TypeArguments::Handle(type_params.bounds());
result = CombineHashes(result, bounds.Hash());
// Do not calculate the hash of the bounds using TypeArguments::Hash(),
// because HashForRange() dereferences TypeRefs which should not be here.
AbstractType& bound = AbstractType::Handle();
for (intptr_t i = 0; i < num_type_params; i++) {
bound = type_params.BoundAt(i);
result = CombineHashes(result, bound.Hash());
}
// Since the default arguments are ignored when comparing two generic
// function types for type equality, the hash does not depend on them.
}
@ -21705,7 +21717,9 @@ AbstractTypePtr TypeParameter::InstantiateFrom(
upper_bound = upper_bound.InstantiateFrom(
instantiator_type_arguments, function_type_arguments,
num_free_fun_type_params, space, trail);
if (upper_bound.ptr() == Type::NeverType()) {
if ((upper_bound.IsTypeRef() &&
TypeRef::Cast(upper_bound).type() == Type::NeverType()) ||
(upper_bound.ptr() == Type::NeverType())) {
// Normalize 'X extends Never' to 'Never'.
result = Type::NeverType();
} else if (upper_bound.ptr() != bound()) {

View file

@ -0,0 +1,15 @@
// Copyright (c) 2021, 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.
abstract class Base<A, B> {}
extension on Object {
B? foo<A extends Base<A, B>, B extends Base<A, B>>(B? orig) {
return null;
}
}
main() {
print(Object().foo);
}