From 01ba02caa924058af539d37970d2b07f22d9fdf7 Mon Sep 17 00:00:00 2001
From: Jonathan Wakely
Date: Thu, 31 Oct 2024 16:29:18 +0000
Subject: [PATCH] libstdc++: Refactor std::hash specializations
This attempts to simplify and clean up our std::hash code. The primary
benefit is improved diagnostics for users when they do something wrong
involving std::hash or unordered containers. An additional benefit is
that for the unstable ABI (--enable-symvers=gnu-versioned-namespace) we
can reduce the memory footprint of several std::hash specializations.
In the current design, __hash_enum is a base class of the std::hash
primary template, but the partial specialization of __hash_enum for
non-enum types is disabled. This means that if a user forgets to
specialize std::hash for their class type (or forgets to use a custom
hash function for unordered containers) they get error messages about
std::__hash_enum not being constructible. This is confusing when there
is no enum type involved: why should users care about __hash_enum not
being constructible if they're not trying to hash enums?
This change makes the std::hash primary template only derive from
__hash_enum when the template argument type is an enum. Otherwise, it
derives directly from a new class template, __hash_not_enabled. This new
class template defines the deleted members that cause a given std::hash
specialization to be a disabled specialization (as per P0513R0). Now
when users try to use a disabled specialization, they get more
descriptive errors that mention __hash_not_enabled instead of
__hash_enum.
Additionally, adjust __hash_base to remove the deprecated result_type
and argument_type typedefs for C++20 and later.
In the current code we use a __poison_hash base class in the std::hash
specializations for std::unique_ptr, std::optional, and std::variant.
The primary template of __poison_hash has deleted special members, which
is used to conditionally disable the derived std::hash specialization.
This can also result in confusing diagnostics, because seeing "poison"
in an enabled specialization is misleading. Only some uses of
__poison_hash actually "poison" anything, i.e. cause a specialization to
be disabled. In other cases it's just an empty base class that does
nothing.
This change removes __poison_hash and changes the std::hash
specializations that were using it to conditionally derive from
__hash_not_enabled instead. When the std::hash specialization is
enabled, there is no more __poison_hash base class. However, to preserve
the ABI properties of those std::hash specializations, we need to
replace __poison_hash with some other empty base class. This is needed
because in the current code std::hash> has
two __poison_hash base classes, which must have unique addresses,
so sizeof(std::hash>) == 2. To preserve
this unfortunate property, a new __hash_empty_base class is used as a
base class to re-introduce du0plicate base classes that increase the
class size. For the unstable ABI we don't use __hash_empty_base so the
std::hash> specializations are always size 1, and
the class hierarchy is much simpler so will compile faster.
Additionally, remove the result_type and argument_type typedefs from all
disabled specializations of std::hash for std::unique_ptr,
std::optional, and std::variant. Those typedefs are useless for disabled
specializations, and although the standard doesn't say they must *not*
be present for disabled specializations, it certainly only requires them
for enabled specializations. Finally, for C++20 the typedefs are also
removed from enabled specializations of std::hash for std::unique_ptr,
std::optional, and std::variant.
libstdc++-v3/ChangeLog:
* doc/xml/manual/evolution.xml: Document removal of nested types
from std::hash specializations.
* doc/html/manual/api.html: Regenerate.
* include/bits/functional_hash.h (__hash_base): Remove
deprecated nested types for C++20.
(__hash_empty_base): Define new class template.
(__is_hash_enabled_for): Define new variable template.
(__poison_hash): Remove.
(__hash_not_enabled): Define new class template.
(__hash_enum): Remove partial specialization for non-enums.
(hash): Derive from __hash_not_enabled for non-enums, instead of
__hash_enum.
* include/bits/unique_ptr.h (__uniq_ptr_hash): Derive from
__hash_base. Conditionally derive from __hash_empty_base.
(__uniq_ptr_hash<>): Remove disabled specialization.
(hash): Do not derive from __hash_base unconditionally.
Conditionally derive from either __uniq_ptr_hash or
__hash_not_enabled.
* include/std/optional (__optional_hash_call_base): Remove.
(__optional_hash): Define new class template.
(hash): Derive from either
(hash): Conditionally derive from either __optional_hash or
__hash_not_enabled. Remove nested typedefs.
* include/std/variant (_Base_dedup): Replace __poison_hash with
__hash_empty_base.
(__variant_hash_call_base_impl): Remove.
(__variant_hash): Define new class template.
(hash): Conditionally derive from either __variant_hash or
__hash_not_enabled. Remove nested typedefs.
* testsuite/20_util/optional/hash.cc: Check whether nested types
are present.
* testsuite/20_util/variant/hash.cc: Likewise.
* testsuite/20_util/optional/hash_abi.cc: New test.
* testsuite/20_util/unique_ptr/hash/abi.cc: New test.
* testsuite/20_util/unique_ptr/hash/types.cc: New test.
* testsuite/20_util/variant/hash_abi.cc: New test.
---
libstdc++-v3/doc/html/manual/api.html | 3 +
libstdc++-v3/doc/xml/manual/evolution.xml | 5 ++
libstdc++-v3/include/bits/functional_hash.h | 47 +++++++-------
libstdc++-v3/include/bits/unique_ptr.h | 18 +++---
libstdc++-v3/include/std/optional | 26 ++++----
libstdc++-v3/include/std/variant | 41 ++++++------
.../testsuite/20_util/optional/hash.cc | 33 ++++++++++
.../testsuite/20_util/optional/hash_abi.cc | 35 ++++++++++
.../testsuite/20_util/unique_ptr/hash/abi.cc | 64 +++++++++++++++++++
.../20_util/unique_ptr/hash/types.cc | 53 +++++++++++++++
.../testsuite/20_util/variant/hash.cc | 34 ++++++++++
.../testsuite/20_util/variant/hash_abi.cc | 48 ++++++++++++++
12 files changed, 344 insertions(+), 63 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/optional/hash_abi.cc
create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc
create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc
create mode 100644 libstdc++-v3/testsuite/20_util/variant/hash_abi.cc
diff --git a/libstdc++-v3/doc/html/manual/api.html b/libstdc++-v3/doc/html/manual/api.html
index 2ccfc07b83e..7a4c7d9fe62 100644
--- a/libstdc++-v3/doc/html/manual/api.html
+++ b/libstdc++-v3/doc/html/manual/api.html
@@ -506,4 +506,7 @@ and removed in C++20:
<cstdalign>
,
<cstdbool>
, and
<ctgmath>
.
+
+Nested result_type
and argument_type
removed from
+std::hash
specializations for C++20.