[vm] Fix equality of uninstantiated generic closures

Equality of uninstantiated generic closures should match equality
of corresponding instantiated generic closures.

We cannot use identity for equality of instantiated generic closures
as distinct instantiations of the same generic closure should be equal.
So, identity shouldn't be used for uninstantiated generic closures
neither.

This change fixes equality for uninstantiated generic closures
and also updates closure hashCode correspondingly.

TEST=language/closure/instantiation_closure_equality_test
TEST=co19/LanguageFeatures/Constructor-tear-offs/equality_*

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

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-release-x64-try
Change-Id: Ieafc052de4a4f5f9ffcd2d9d26cb36a209c0e127
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287581
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2023-03-08 21:44:56 +00:00 committed by Commit Queue
parent 18d414d24d
commit 8ff777e61f
2 changed files with 29 additions and 17 deletions

View file

@ -44,7 +44,7 @@ static bool ClosureEqualsHelper(Zone* zone,
const auto& func_b = Function::Handle(zone, other_closure.function());
// Check that functions match.
if (func_a.ptr() != func_b.ptr()) {
// Closure functions that are not implicit closures (tear-offs) are unique.
// Non-implicit closures taken from different functions are not equal.
if (!func_a.IsImplicitClosureFunction() ||
!func_b.IsImplicitClosureFunction()) {
return false;
@ -80,17 +80,27 @@ static bool ClosureEqualsHelper(Zone* zone,
const Context& context_b = Context::Handle(zone, other_closure.context());
return context_a.At(0) == context_b.At(0);
}
} else {
// Non-identical closures which are not tear-offs can be equal only if
// they are different instantiations of the same generic closure.
if (!func_a.IsGeneric() || receiver.IsGeneric() ||
(receiver.context() != other_closure.context()) ||
} else if (func_a.IsGeneric()) {
// Additional constraints for closures of generic functions:
// (1) Different instantiations of the same generic closure
// with the same type arguments should be equal.
// This means that instantiated generic closures are not unique
// and equality of instantiated generic closures should not be
// based on identity.
// (2) Instantiations of non-equal generic closures should be non-equal.
// This means that equality of non-instantiated generic closures
// should not be based on identity too as it won't match equality
// after instantiation.
if ((receiver.context() != other_closure.context()) ||
(receiver.instantiator_type_arguments() !=
other_closure.instantiator_type_arguments()) ||
(receiver.function_type_arguments() !=
other_closure.function_type_arguments())) {
return false;
}
} else {
// Closures of non-generic functions are unique.
return false;
}
return true;
}

View file

@ -26529,24 +26529,26 @@ uword Closure::ComputeHash() const {
Zone* zone = thread->zone();
const Function& func = Function::Handle(zone, function());
uint32_t result = 0;
if (func.IsImplicitInstanceClosureFunction()) {
// Implicit instance closures are not unique, so combine function's hash
// code, delayed type arguments hash code (if generic), and identityHashCode
// of cached receiver.
if (func.IsImplicitClosureFunction() || func.IsGeneric()) {
// Combine function's hash code, delayed type arguments hash code
// (if generic), and identityHashCode of cached receiver (if implicit
// instance closure).
result = static_cast<uint32_t>(func.Hash());
if (func.IsGeneric()) {
const TypeArguments& delayed_type_args =
TypeArguments::Handle(zone, delayed_type_arguments());
result = CombineHashes(result, delayed_type_args.Hash());
}
const Context& context = Context::Handle(zone, this->context());
const Instance& receiver =
Instance::Handle(zone, Instance::RawCast(context.At(0)));
const Integer& receiverHash =
Integer::Handle(zone, receiver.IdentityHashCode(thread));
result = CombineHashes(result, receiverHash.AsTruncatedUint32Value());
if (func.IsImplicitInstanceClosureFunction()) {
const Context& context = Context::Handle(zone, this->context());
const Instance& receiver =
Instance::Handle(zone, Instance::RawCast(context.At(0)));
const Integer& receiverHash =
Integer::Handle(zone, receiver.IdentityHashCode(thread));
result = CombineHashes(result, receiverHash.AsTruncatedUint32Value());
}
} else {
// Explicit closures and implicit static closures are unique,
// Non-implicit closures of non-generic functions are unique,
// so identityHashCode of closure object is good enough.
const Integer& identityHash =
Integer::Handle(zone, this->IdentityHashCode(thread));