Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2226220/?format=api
{ "id": 2226220, "url": "http://patchwork.ozlabs.org/api/patches/2226220/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.1@forge-stage.sourceware.org/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api", "name": "GNU Compiler Collection", "link_name": "gcc", "list_id": "gcc-patches.gcc.gnu.org", "list_email": "gcc-patches@gcc.gnu.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.1@forge-stage.sourceware.org>", "list_archive_url": null, "date": "2026-04-22T10:26:26", "name": "[v1,1/1] libstdc++: Refactor std::hash specializations", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "6f03f6a830cb95b96bfa80b8cea5f478257cb71e", "submitter": { "id": 93210, "url": "http://patchwork.ozlabs.org/api/people/93210/?format=api", "name": "Jonathan Wakely via Sourceware Forge", "email": "forge-bot+redi@forge-stage.sourceware.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.1@forge-stage.sourceware.org/mbox/", "series": [ { "id": 500974, "url": "http://patchwork.ozlabs.org/api/series/500974/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=500974", "date": "2026-04-22T10:26:26", "name": "libstdc++: Refactor std::hash specializations", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/500974/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2226220/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2226220/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org; dmarc=none (p=none dis=none)\n header.from=forge-stage.sourceware.org", "sourceware.org;\n spf=pass smtp.mailfrom=forge-stage.sourceware.org", "server2.sourceware.org;\n arc=none smtp.remote-ip=38.145.34.39" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0wml44WJz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 20:43:19 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 360764BB3BFE\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 10:43:17 +0000 (GMT)", "from forge-stage.sourceware.org (vm08.sourceware.org [38.145.34.39])\n by sourceware.org (Postfix) with ESMTPS id 47D2C4B92097\n for <gcc-patches@gcc.gnu.org>; Wed, 22 Apr 2026 10:27:19 +0000 (GMT)", "from forge-stage.sourceware.org (localhost [IPv6:::1])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256)\n (No client certificate requested)\n by forge-stage.sourceware.org (Postfix) with ESMTPS id 1EB8B405AA\n for <gcc-patches@gcc.gnu.org>; Wed, 22 Apr 2026 10:27:19 +0000 (UTC)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org 360764BB3BFE", "OpenDKIM Filter v2.11.0 sourceware.org 47D2C4B92097" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 47D2C4B92097", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 47D2C4B92097", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776853639; cv=none;\n b=S/ruhXbRpc97d63m6GSK7MeKd8pUfYGjLyF1Cqdki5h9c4eHleBdJAmTchYyqeFWtxGB5hZYs7+6I+SnElqgS8HSOBCL+ToFWSav1XvgdqOcTgNLCnIS9BQLADyR61HBZugA2G0gI9WRnP0NYlgwAEsODzzdbJ60ApOwIn+xU+Q=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776853639; c=relaxed/simple;\n bh=0vhC0Fm3QRtfi347K8aonF4Bj+HFF8JxLe45NATaksc=;\n h=From:Date:Subject:To:Message-ID;\n b=vUUgHDDgwPlwwxJ5M2OhMDwto2mePV/KO+K4t950LyZLCLPaMlosBwaa2/CbP811IN2LKjvI2LWHECxKmYa/nkgQbMIVc2MNLPLsw688l71RDstR3BQLKzsUT48Xg0On5gsm0ZigvaLpMP+jk2/EeSJylsh0BepQVpCF7ZAX6zQ=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "From": "Jonathan Wakely via Sourceware Forge\n <forge-bot+redi@forge-stage.sourceware.org>", "Date": "Wed, 22 Apr 2026 10:26:26 +0000", "Subject": "[PATCH v1 1/1] libstdc++: Refactor std::hash specializations", "To": "gcc-patches mailing list <gcc-patches@gcc.gnu.org>", "Message-ID": "\n <bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.1@forge-stage.sourceware.org>", "X-Mailer": "batrachomyomachia", "X-Pull-Request-Organization": "gcc", "X-Pull-Request-Repository": "gcc-TEST", "X-Pull-Request": "https://forge.sourceware.org/gcc/gcc-TEST/pulls/16", "References": "\n <bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.0@forge-stage.sourceware.org>", "In-Reply-To": "\n <bmm.hhub9plyek.gcc.gcc-TEST.redi.16.1.0@forge-stage.sourceware.org>", "X-Patch-URL": "\n https://forge.sourceware.org/redi/gcc/commit/34be1a8927df3aad5db13394a813c60dd83c4e14", "X-BeenThere": "gcc-patches@gcc.gnu.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>", "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>", "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>", "List-Post": "<mailto:gcc-patches@gcc.gnu.org>", "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>", "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>", "Reply-To": "gcc-patches mailing list <gcc-patches@gcc.gnu.org>, redi@gcc.gnu.org", "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org" }, "content": "From: Jonathan Wakely <jwakely@redhat.com>\n\nThis attempts to simplify and clean up our std::hash code. The primary\nbenefit is improved diagnostics for users when they do something wrong\ninvolving std::hash or unordered containers. An additional benefit is\nthat for the unstable ABI (--enable-symvers=gnu-versioned-namespace) we\ncan reduce the memory footprint of several std::hash specializations.\n\nIn the current design, __hash_enum is a base class of the std::hash\nprimary template, but the partial specialization of __hash_enum for\nnon-enum types is disabled. This means that if a user forgets to\nspecialize std::hash for their class type (or forgets to use a custom\nhash function for unordered containers) they get error messages about\nstd::__hash_enum not being constructible. This is confusing when there\nis no enum type involved: why should users care about __hash_enum not\nbeing constructible if they're not trying to hash enums?\n\nThis change makes the std::hash primary template only derive from\n__hash_enum when the template argument type is an enum. Otherwise, it\nderives directly from a new class template, __hash_not_enabled. This new\nclass template defines the deleted members that cause a given std::hash\nspecialization to be a disabled specialization (as per P0513R0). Now\nwhen users try to use a disabled specialization, they get more\ndescriptive errors that mention __hash_not_enabled instead of\n__hash_enum.\n\nAdditionally, adjust __hash_base to remove the deprecated result_type\nand argument_type typedefs for C++20 and later.\n\nIn the current code we use a __poison_hash base class in the std::hash\nspecializations for std::unique_ptr, std::optional, and std::variant.\nThe primary template of __poison_hash has deleted special members, which\nis used to conditionally disable the derived std::hash specialization.\nThis can also result in confusing diagnostics, because seeing \"poison\"\nin an enabled specialization is misleading. Only some uses of\n__poison_hash actually \"poison\" anything, i.e. cause a specialization to\nbe disabled. In other cases it's just an empty base class that does\nnothing.\n\nThis change removes __poison_hash and changes the std::hash\nspecializations that were using it to conditionally derive from\n__hash_not_enabled instead. When the std::hash specialization is\nenabled, there is no more __poison_hash base class. However, to preserve\nthe ABI properties of those std::hash specializations, we need to\nreplace __poison_hash with some other empty base class. This is needed\nbecause in the current code std::hash<std::variant<int, const int>> has\ntwo __poison_hash<int> base classes, which must have unique addresses,\nso sizeof(std::hash<std::variant<int, const int>>) == 2. To preserve\nthis unfortunate property, a new __hash_empty_base class is used as a\nbase class to re-introduce du0plicate base classes that increase the\nclass size. For the unstable ABI we don't use __hash_empty_base so the\nstd::hash<std::variant<T...>> specializations are always size 1, and\nthe class hierarchy is much simpler so will compile faster.\n\nAdditionally, remove the result_type and argument_type typedefs from all\ndisabled specializations of std::hash for std::unique_ptr,\nstd::optional, and std::variant. Those typedefs are useless for disabled\nspecializations, and although the standard doesn't say they must *not*\nbe present for disabled specializations, it certainly only requires them\nfor enabled specializations. Finally, for C++20 the typedefs are also\nremoved from enabled specializations of std::hash for std::unique_ptr,\nstd::optional, and std::variant.\n\nlibstdc++-v3/ChangeLog:\n\n\t* doc/xml/manual/evolution.xml: Document removal of nested types\n\tfrom std::hash specializations.\n\t* doc/html/manual/api.html: Regenerate.\n\t* include/bits/functional_hash.h (__hash_base): Remove\n\tdeprecated nested types for C++20.\n\t(__hash_empty_base): Define new class template.\n\t(__is_hash_enabled_for): Define new variable template.\n\t(__poison_hash): Remove.\n\t(__hash_not_enabled): Define new class template.\n\t(__hash_enum): Remove partial specialization for non-enums.\n\t(hash): Derive from __hash_not_enabled for non-enums, instead of\n\t__hash_enum.\n\t* include/bits/unique_ptr.h (__uniq_ptr_hash): Derive from\n\t__hash_base. Conditionally derive from __hash_empty_base.\n\t(__uniq_ptr_hash<>): Remove disabled specialization.\n\t(hash): Do not derive from __hash_base unconditionally.\n\tConditionally derive from either __uniq_ptr_hash or\n\t__hash_not_enabled.\n\t* include/std/optional (__optional_hash_call_base): Remove.\n\t(__optional_hash): Define new class template.\n\t(hash): Derive from either\n\t(hash): Conditionally derive from either __optional_hash or\n\t__hash_not_enabled. Remove nested typedefs.\n\t* include/std/variant (_Base_dedup): Replace __poison_hash with\n\t__hash_empty_base.\n\t(__variant_hash_call_base_impl): Remove.\n\t(__variant_hash): Define new class template.\n\t(hash): Conditionally derive from either __variant_hash or\n\t__hash_not_enabled. Remove nested typedefs.\n\t* testsuite/20_util/optional/hash.cc: Check whether nested types\n\tare present.\n\t* testsuite/20_util/variant/hash.cc: Likewise.\n\t* testsuite/20_util/optional/hash_abi.cc: New test.\n\t* testsuite/20_util/unique_ptr/hash/abi.cc: New test.\n\t* testsuite/20_util/unique_ptr/hash/types.cc: New test.\n\t* testsuite/20_util/variant/hash_abi.cc: New test.\n---\n libstdc++-v3/doc/html/manual/api.html | 3 +\n libstdc++-v3/doc/xml/manual/evolution.xml | 5 ++\n libstdc++-v3/include/bits/functional_hash.h | 47 +++++++-------\n libstdc++-v3/include/bits/unique_ptr.h | 18 +++---\n libstdc++-v3/include/std/optional | 26 ++++----\n libstdc++-v3/include/std/variant | 41 ++++++------\n .../testsuite/20_util/optional/hash.cc | 33 ++++++++++\n .../testsuite/20_util/optional/hash_abi.cc | 35 ++++++++++\n .../testsuite/20_util/unique_ptr/hash/abi.cc | 64 +++++++++++++++++++\n .../20_util/unique_ptr/hash/types.cc | 53 +++++++++++++++\n .../testsuite/20_util/variant/hash.cc | 34 ++++++++++\n .../testsuite/20_util/variant/hash_abi.cc | 48 ++++++++++++++\n 12 files changed, 344 insertions(+), 63 deletions(-)\n create mode 100644 libstdc++-v3/testsuite/20_util/optional/hash_abi.cc\n create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc\n create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc\n create mode 100644 libstdc++-v3/testsuite/20_util/variant/hash_abi.cc", "diff": "diff --git a/libstdc++-v3/doc/html/manual/api.html b/libstdc++-v3/doc/html/manual/api.html\nindex 2ccfc07b83e5..7a4c7d9fe620 100644\n--- a/libstdc++-v3/doc/html/manual/api.html\n+++ b/libstdc++-v3/doc/html/manual/api.html\n@@ -506,4 +506,7 @@ and removed in C++20:\n <code class=\"filename\"><cstdalign></code>,\n <code class=\"filename\"><cstdbool></code>, and\n <code class=\"filename\"><ctgmath></code>.\n+</p><p>\n+Nested <code class=\"code\">result_type</code> and <code class=\"code\">argument_type</code> removed from\n+<code class=\"classname\">std::hash</code> specializations for C++20.\n </p></div></div><div class=\"navfooter\"><hr /><table width=\"100%\" summary=\"Navigation footer\"><tr><td width=\"40%\" align=\"left\"><a accesskey=\"p\" href=\"abi.html\">Prev</a> </td><td width=\"20%\" align=\"center\"><a accesskey=\"u\" href=\"appendix_porting.html\">Up</a></td><td width=\"40%\" align=\"right\"> <a accesskey=\"n\" href=\"backwards.html\">Next</a></td></tr><tr><td width=\"40%\" align=\"left\" valign=\"top\">ABI Policy and Guidelines </td><td width=\"20%\" align=\"center\"><a accesskey=\"h\" href=\"../index.html\">Home</a></td><td width=\"40%\" align=\"right\" valign=\"top\"> Backwards Compatibility</td></tr></table></div></body></html>\n\\ No newline at end of file\ndiff --git a/libstdc++-v3/doc/xml/manual/evolution.xml b/libstdc++-v3/doc/xml/manual/evolution.xml\nindex 6b134de0e71f..84f8ab9d2981 100644\n--- a/libstdc++-v3/doc/xml/manual/evolution.xml\n+++ b/libstdc++-v3/doc/xml/manual/evolution.xml\n@@ -1145,6 +1145,11 @@ and removed in C++20:\n <filename class=\"headerfile\"><ctgmath></filename>.\n </para>\n \n+<para>\n+Nested <code>result_type</code> and <code>argument_type</code> removed from\n+<classname>std::hash</classname> specializations for C++20.\n+</para>\n+\n </section>\n \n </section>\ndiff --git a/libstdc++-v3/include/bits/functional_hash.h b/libstdc++-v3/include/bits/functional_hash.h\nindex e7d8c6c20545..6815edeb8333 100644\n--- a/libstdc++-v3/include/bits/functional_hash.h\n+++ b/libstdc++-v3/include/bits/functional_hash.h\n@@ -52,43 +52,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n template<typename _Result, typename _Arg>\n struct __hash_base\n {\n+#if __cplusplus < 202002L\n typedef _Result result_type _GLIBCXX17_DEPRECATED;\n typedef _Arg argument_type _GLIBCXX17_DEPRECATED;\n+#endif\n };\n \n+#if ! _GLIBCXX_INLINE_VERSION\n+ // Some std::hash specializations inherit this for ABI compatibility reasons.\n+ template<typename _Tp> struct __hash_empty_base { };\n+#endif\n+\n /// Primary class template hash.\n template<typename _Tp>\n struct hash;\n \n+#pragma GCC diagnostic push\n+#pragma GCC diagnostic ignored \"-Wc++14-extensions\"\n template<typename _Tp, typename = void>\n- struct __poison_hash\n- {\n- static constexpr bool __enable_hash_call = false;\n- private:\n- // Private rather than deleted to be non-trivially-copyable.\n- __poison_hash(__poison_hash&&);\n- ~__poison_hash();\n- };\n+ constexpr bool __is_hash_enabled_for = false;\n \n template<typename _Tp>\n- struct __poison_hash<_Tp, __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>\n- {\n- static constexpr bool __enable_hash_call = true;\n- };\n+ constexpr bool\n+ __is_hash_enabled_for<_Tp,\n+\t\t\t __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>\n+ = true;\n+#pragma GCC diagnostic pop\n \n- // Helper struct for SFINAE-poisoning non-enum types.\n- template<typename _Tp, bool = is_enum<_Tp>::value>\n- struct __hash_enum\n+ // Helper struct for defining disabled specializations of std::hash.\n+ template<typename _Tp>\n+ struct __hash_not_enabled\n {\n- private:\n- // Private rather than deleted to be non-trivially-copyable.\n- __hash_enum(__hash_enum&&);\n- ~__hash_enum();\n+ __hash_not_enabled(__hash_not_enabled&&) = delete;\n+ ~__hash_not_enabled() = delete;\n };\n \n // Helper struct for hash with enum types.\n- template<typename _Tp>\n- struct __hash_enum<_Tp, true> : public __hash_base<size_t, _Tp>\n+ template<typename _Tp, bool = true>\n+ struct __hash_enum : public __hash_base<size_t, _Tp>\n {\n size_t\n operator()(_Tp __val) const noexcept\n@@ -99,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n };\n \n /// Primary class template hash, usable for enum types only.\n- // Use with non-enum types still SFINAES.\n template<typename _Tp>\n- struct hash : __hash_enum<_Tp>\n+ struct hash\n+ : __conditional_t<__is_enum(_Tp), __hash_enum<_Tp>, __hash_not_enabled<_Tp>>\n { };\n \n /// Partial specializations for pointer types.\ndiff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h\nindex 182173aa8571..cf24ba80a61e 100644\n--- a/libstdc++-v3/include/bits/unique_ptr.h\n+++ b/libstdc++-v3/include/bits/unique_ptr.h\n@@ -1012,11 +1012,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n /// @} relates unique_ptr\n \n /// @cond undocumented\n- template<typename _Up, typename _Ptr = typename _Up::pointer,\n-\t bool = __poison_hash<_Ptr>::__enable_hash_call>\n+ template<typename _Up, typename _Ptr = typename _Up::pointer>\n struct __uniq_ptr_hash\n+ : public __hash_base<size_t, _Up>\n #if ! _GLIBCXX_INLINE_VERSION\n- : private __poison_hash<_Ptr>\n+ , private __hash_empty_base<_Ptr>\n #endif\n {\n size_t\n@@ -1025,17 +1025,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n { return hash<_Ptr>()(__u.get()); }\n };\n \n- template<typename _Up, typename _Ptr>\n- struct __uniq_ptr_hash<_Up, _Ptr, false>\n- : private __poison_hash<_Ptr>\n- { };\n+ template<typename _Up>\n+ using __uniq_ptr_hash_base\n+ = __conditional_t<__is_hash_enabled_for<typename _Up::pointer>,\n+\t\t\t __uniq_ptr_hash<_Up>,\n+\t\t\t __hash_not_enabled<typename _Up::pointer>>;\n /// @endcond\n \n /// std::hash specialization for unique_ptr.\n template<typename _Tp, typename _Dp>\n struct hash<unique_ptr<_Tp, _Dp>>\n- : public __hash_base<size_t, unique_ptr<_Tp, _Dp>>,\n- public __uniq_ptr_hash<unique_ptr<_Tp, _Dp>>\n+ : public __uniq_ptr_hash_base<unique_ptr<_Tp, _Dp>>\n { };\n \n #ifdef __glibcxx_make_unique // C++ >= 14 && HOSTED\ndiff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional\nindex 2e663d13f86a..b8eedeec7817 100644\n--- a/libstdc++-v3/include/std/optional\n+++ b/libstdc++-v3/include/std/optional\n@@ -1732,10 +1732,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n \n // Hash.\n \n- template<typename _Tp, typename _Up = remove_const_t<_Tp>,\n-\t bool = __poison_hash<_Up>::__enable_hash_call>\n- struct __optional_hash_call_base\n+ template<typename _Tp, typename _Up = remove_const_t<_Tp>>\n+ struct __optional_hash\n+#if ! _GLIBCXX_INLINE_VERSION\n+ : public __hash_empty_base<_Up>\n+#endif\n {\n+#if __cplusplus < 202002L\n+ using result_type [[__deprecated__]] = size_t;\n+ using argument_type [[__deprecated__]] = optional<_Tp>;\n+#endif\n+\n size_t\n operator()(const optional<_Tp>& __t) const\n noexcept(noexcept(hash<_Up>{}(*__t)))\n@@ -1747,17 +1754,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n }\n };\n \n- template<typename _Tp, typename _Up>\n- struct __optional_hash_call_base<_Tp, _Up, false> {};\n-\n template<typename _Tp>\n struct hash<optional<_Tp>>\n- : private __poison_hash<remove_const_t<_Tp>>,\n- public __optional_hash_call_base<_Tp>\n- {\n- using result_type [[__deprecated__]] = size_t;\n- using argument_type [[__deprecated__]] = optional<_Tp>;\n- };\n+ : public __conditional_t<__is_hash_enabled_for<remove_const_t<_Tp>>,\n+\t\t\t __optional_hash<_Tp>,\n+\t\t\t __hash_not_enabled<_Tp>>\n+ { };\n \n template<typename _Tp>\n struct __is_fast_hash<hash<optional<_Tp>>> : __is_fast_hash<hash<_Tp>>\ndiff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant\nindex bd0f9c3252a5..32e539980839 100644\n--- a/libstdc++-v3/include/std/variant\n+++ b/libstdc++-v3/include/std/variant\n@@ -1094,7 +1094,8 @@ namespace __variant\n \t= __gen_vtable_impl<_Array_type, std::index_sequence<>>::_S_apply();\n };\n \n- template<size_t _Np, typename _Tp>\n+#if ! _GLIBCXX_INLINE_VERSION\n+ template<size_t _Nm, typename _Tp>\n struct _Base_dedup : public _Tp { };\n \n template<typename _Variant, typename __indices>\n@@ -1103,7 +1104,9 @@ namespace __variant\n template<typename... _Types, size_t... __indices>\n struct _Variant_hash_base<variant<_Types...>,\n \t\t\t std::index_sequence<__indices...>>\n- : _Base_dedup<__indices, __poison_hash<remove_const_t<_Types>>>... { };\n+ : _Base_dedup<__indices, __hash_empty_base<remove_const_t<_Types>>>...\n+ { };\n+#endif\n \n // Equivalent to decltype(get<_Np>(as-variant(declval<_Variant>())))\n template<size_t _Np, typename _Variant,\n@@ -1987,9 +1990,14 @@ namespace __detail::__variant\n #endif\n \n /// @cond undocumented\n- template<bool, typename... _Types>\n- struct __variant_hash_call_base_impl\n+ template<typename... _Types>\n+ struct __variant_hash\n {\n+#if __cplusplus < 202002L\n+ using result_type [[__deprecated__]] = size_t;\n+ using argument_type [[__deprecated__]] = variant<_Types...>;\n+#endif\n+\n size_t\n operator()(const variant<_Types...>& __t) const\n noexcept((is_nothrow_invocable_v<hash<decay_t<_Types>>, _Types> && ...))\n@@ -2009,31 +2017,26 @@ namespace __detail::__variant\n \treturn __ret;\n }\n };\n-\n- template<typename... _Types>\n- struct __variant_hash_call_base_impl<false, _Types...> {};\n-\n- template<typename... _Types>\n- using __variant_hash_call_base =\n- __variant_hash_call_base_impl<(__poison_hash<remove_const_t<_Types>>::\n-\t\t\t\t __enable_hash_call &&...), _Types...>;\n /// @endcond\n \n template<typename... _Types>\n struct hash<variant<_Types...>>\n- : private __detail::__variant::_Variant_hash_base<\n-\tvariant<_Types...>, std::index_sequence_for<_Types...>>,\n- public __variant_hash_call_base<_Types...>\n- {\n- using result_type [[__deprecated__]] = size_t;\n- using argument_type [[__deprecated__]] = variant<_Types...>;\n- };\n+ : __conditional_t<(__is_hash_enabled_for<remove_const_t<_Types>> && ...),\n+\t\t __variant_hash<_Types...>,\n+\t\t __hash_not_enabled<variant<_Types...>>>\n+#if ! _GLIBCXX_INLINE_VERSION\n+ , __detail::__variant::_Variant_hash_base<variant<_Types...>,\n+\t\t\t\t\t\tindex_sequence_for<_Types...>>\n+#endif\n+ { };\n \n template<>\n struct hash<monostate>\n {\n+#if __cplusplus < 202002L\n using result_type [[__deprecated__]] = size_t;\n using argument_type [[__deprecated__]] = monostate;\n+#endif\n \n size_t\n operator()(const monostate&) const noexcept\ndiff --git a/libstdc++-v3/testsuite/20_util/optional/hash.cc b/libstdc++-v3/testsuite/20_util/optional/hash.cc\nindex e441c87e0c95..607f56459aba 100644\n--- a/libstdc++-v3/testsuite/20_util/optional/hash.cc\n+++ b/libstdc++-v3/testsuite/20_util/optional/hash.cc\n@@ -49,3 +49,36 @@ int main()\n std::optional<const int> x3 = x2;\n VERIFY(std::hash<int>()(x) == std::hash<std::optional<const int>>()(x3));\n }\n+\n+// Check for presence/absence of nested types.\n+\n+template<typename T> using res_type = typename std::hash<T>::result_type;\n+template<typename T> using arg_type = typename std::hash<T>::argument_type;\n+\n+template<typename Opt, typename = void>\n+constexpr bool has_res_type = false;\n+template<typename Opt>\n+constexpr bool has_res_type<Opt, std::void_t<res_type<Opt>>> = true;\n+template<typename Opt, typename = void>\n+constexpr bool has_arg_type = false;\n+template<typename Opt>\n+constexpr bool has_arg_type<Opt, std::void_t<arg_type<Opt>>> = true;\n+\n+template<typename T>\n+constexpr bool has_no_types\n+ = ! has_res_type<std::optional<T>> && ! has_arg_type<std::optional<T>>;\n+\n+#if __cplusplus >= 202002L\n+// Nested types result_type and argument_type are not present in C++20\n+static_assert( has_no_types<int> );\n+static_assert( has_no_types<double> );\n+#else\n+// Nested types result_type and argument_type are deprecated in C++17.\n+using R1 = std::hash<std::optional<int>>::result_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using A1 = std::hash<std::optional<int>>::argument_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using R2 = std::hash<std::optional<char>>::result_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using A2 = std::hash<std::optional<char>>::argument_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+#endif\n+\n+// Disabled specializations do not have the nested types.\n+static_assert( has_no_types<S> );\ndiff --git a/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc b/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc\nnew file mode 100644\nindex 000000000000..78e992c4e41d\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc\n@@ -0,0 +1,35 @@\n+// { dg-do compile { target c++17 } }\n+\n+#include <optional>\n+\n+struct S { }; // std::hash<S> is a disabled specialization.\n+\n+template<typename T>\n+constexpr std::size_t hash_size = sizeof(std::hash<std::optional<T>>);\n+\n+template<typename... Ts>\n+struct MultiHash : std::hash<std::optional<Ts>>...\n+{ };\n+\n+#if _GLIBCXX_INLINE_VERSION\n+// For the unstable ABI the size should always be one.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == 1;\n+#else\n+// For the default ABI, each std::hash<std::optional<T>> specialization has\n+// a base class of type __hash_empty_base<remove_cv_t<T>> and if\n+// the same type occurs more than once they must have unique addresses.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == Size;\n+#endif\n+\n+static_assert( check_hash_size<1, int> );\n+static_assert( check_hash_size<1, int, long, double> );\n+static_assert( check_hash_size<2, int, const int> );\n+static_assert( check_hash_size<2, int, long, const int> );\n+\n+static_assert( check_hash_size<1, S> );\n+static_assert( check_hash_size<1, int, S> );\n+static_assert( check_hash_size<2, int, S, const int> );\n+static_assert( check_hash_size<2, int, S, const int, const S> );\n+static_assert( check_hash_size<2, int, S, const S, const int> );\ndiff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc\nnew file mode 100644\nindex 000000000000..9c0a32c49c92\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc\n@@ -0,0 +1,64 @@\n+// { dg-do compile { target c++17 } }\n+\n+#include <memory>\n+\n+struct S { }; // std::hash<S> is a disabled specialization.\n+\n+template<typename T, typename D = std::default_delete<T>>\n+constexpr std::size_t hash_size = sizeof(std::hash<std::unique_ptr<T, D>>);\n+\n+template<typename... Ts>\n+struct MultiHash : std::hash<std::unique_ptr<Ts>>...\n+{ };\n+\n+#if _GLIBCXX_INLINE_VERSION\n+// For the unstable ABI the size should always be one.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == 1;\n+#else\n+// For the default ABI, each std::hash<std::unique_ptr<T,D >> specialization\n+// has a base class of type __hash_empty_base<D::pointer> and if\n+// the same type occurs more than once they must have unique addresses.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == Size;\n+#endif\n+\n+// All these types have distinct D::pointer types, so no duplicate base classes\n+static_assert( check_hash_size<1, int>, \"\" );\n+static_assert( check_hash_size<1, int, long, double>, \"\" );\n+static_assert( check_hash_size<1, int, const int>, \"\" );\n+static_assert( check_hash_size<1, int, long, const int>, \"\" );\n+// Likewise for these disabled specializations:\n+static_assert( check_hash_size<1, S>, \"\" );\n+static_assert( check_hash_size<1, int, S>, \"\" );\n+static_assert( check_hash_size<1, int, S, const int>, \"\" );\n+static_assert( check_hash_size<1, int, S, const int, const S>, \"\" );\n+static_assert( check_hash_size<1, int, S, const S, const int>, \"\" );\n+\n+// But this has two base classes of type __hash_empty_base<int*>:\n+static_assert( check_hash_size<2, int, int[]>, \"\" );\n+static_assert( check_hash_size<2, int, int[], const int>, \"\" );\n+// And this has two base classes of type __hash_not_enabled<S*>:\n+static_assert( check_hash_size<2, S, S[]>, \"\" );\n+static_assert( check_hash_size<2, S, S[], const S>, \"\" );\n+\n+struct Del : std::default_delete<int> { };\n+using P = std::unique_ptr<int>;\n+using PD = std::unique_ptr<int, Del>;\n+using PC = std::unique_ptr<int, std::default_delete<const int>>;\n+using PA = std::unique_ptr<int[]>;\n+struct HashClash\n+: std::hash<P>, std::hash<PD>, std::hash<PC>, std::hash<PA>\n+{ };\n+#if _GLIBCXX_INLINE_VERSION\n+static_assert(sizeof(HashClash) == 1, \"No duplicate bases for unstable ABI\");\n+#else\n+static_assert(sizeof(HashClash) == 4, \"four __hash_empty_base<int*> bases\");\n+#endif\n+\n+struct Del2 : std::default_delete<const int> { using pointer = const int*; };\n+using PD2 = std::unique_ptr<int, Del2>;\n+struct Hash2\n+: std::hash<PD>, std::hash<PD2>\n+{ };\n+static_assert(sizeof(Hash2) == 1, \"No duplicate bases\");\ndiff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc\nnew file mode 100644\nindex 000000000000..dbe3d8084fe8\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc\n@@ -0,0 +1,53 @@\n+// { dg-do compile { target c++11 } }\n+\n+#include <memory>\n+\n+\n+// Check for presence/absence of nested types.\n+\n+template<typename T> using res_type = typename std::hash<T>::result_type;\n+template<typename T> using arg_type = typename std::hash<T>::argument_type;\n+\n+template<typename UniqPtr, typename = void>\n+constexpr bool\n+has_res_type(...)\n+{ return false; }\n+\n+template<typename UniqPtr>\n+constexpr typename std::is_void<res_type<UniqPtr>>::value_type // i.e. bool\n+has_res_type()\n+{ return true; }\n+\n+template<typename UniqPtr, typename = void>\n+constexpr bool\n+has_arg_type(...)\n+{ return false; }\n+\n+template<typename UniqPtr>\n+constexpr typename std::is_void<arg_type<UniqPtr>>::value_type // i.e. bool\n+has_arg_type()\n+{ return true; }\n+\n+template<typename UniqPtr>\n+constexpr bool\n+has_no_types()\n+{ return ! has_res_type<UniqPtr>() && ! has_arg_type<UniqPtr>(); }\n+\n+#if __cplusplus >= 202002L\n+// Nested types result_type and argument_type are not present in C++20\n+static_assert( has_no_types<std::unique_ptr<int>>() );\n+static_assert( has_no_types<std::unique_ptr<double>>() );\n+#else\n+// Nested types result_type and argument_type are deprecated in C++17.\n+using R1 = std::hash<std::unique_ptr<int>>::result_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using A1 = std::hash<std::unique_ptr<int>>::argument_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+#endif\n+\n+struct S { };\n+template<> struct std::hash<S*> // disabled specialization\n+{\n+ hash(hash&&) = delete;\n+ ~hash() = delete;\n+};\n+// Disabled specializations do not have the nested types.\n+static_assert( has_no_types<std::unique_ptr<S>>(), \"disabled specialization\" );\ndiff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc\nindex f3ab4e0d9cab..52fc759fc34e 100644\n--- a/libstdc++-v3/testsuite/20_util/variant/hash.cc\n+++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc\n@@ -48,3 +48,37 @@ int main()\n std::variant<int> x2 = 42;\n VERIFY(std::hash<int>()(x) == std::hash<std::variant<int>>()(x2));\n }\n+\n+// Check for presence/absence of nested types.\n+\n+template<typename T> using res_type = typename std::hash<T>::result_type;\n+template<typename T> using arg_type = typename std::hash<T>::argument_type;\n+\n+template<typename Variant, typename = void>\n+constexpr bool has_res_type = false;\n+template<typename Variant>\n+constexpr bool has_res_type<Variant, std::void_t<res_type<Variant>>> = true;\n+template<typename Variant, typename = void>\n+constexpr bool has_arg_type = false;\n+template<typename Variant>\n+constexpr bool has_arg_type<Variant, std::void_t<arg_type<Variant>>> = true;\n+\n+template<typename... Ts>\n+constexpr bool has_no_types\n+ = ! has_res_type<std::variant<Ts...>> && ! has_arg_type<std::variant<Ts...>>;\n+\n+#if __cplusplus >= 202002L\n+// Nested types result_type and argument_type are not present in C++20\n+static_assert( has_no_types<int> );\n+static_assert( has_no_types<int, double> );\n+#else\n+// Nested types result_type and argument_type are deprecated in C++17.\n+using R1 = std::hash<std::variant<int>>::result_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using A1 = std::hash<std::variant<int>>::argument_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using R2 = std::hash<std::variant<char, int>>::result_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+using A2 = std::hash<std::variant<char, int>>::argument_type; // { dg-warning \"deprecated\" \"\" { target c++17_only } }\n+#endif\n+\n+// Disabled specializations do not have the nested types.\n+static_assert( has_no_types<S> );\n+static_assert( has_no_types<int, S> );\ndiff --git a/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc b/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc\nnew file mode 100644\nindex 000000000000..19cf72b54de6\n--- /dev/null\n+++ b/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc\n@@ -0,0 +1,48 @@\n+// { dg-options \"-Wdeprecated\" }\n+// { dg-do compile { target c++17 } }\n+\n+#include <variant>\n+\n+struct S { }; // std::hash<S> is a disabled specialization.\n+\n+// Test std::hash size\n+\n+template<typename... Ts>\n+constexpr std::size_t hash_size = sizeof(std::hash<std::variant<Ts...>>);\n+\n+#if _GLIBCXX_INLINE_VERSION\n+// For the unstable ABI the size should always be one.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = hash_size<Ts...> == 1;\n+#else\n+// For the default ABI, the std::hash specialization has sizeof...(Ts)\n+// base classes of types __hash_empty_base<remove_cv_t<Ts>>... and if\n+// the same type occurs more than once they must have unique addresses.\n+template<std::size_t Size, typename... Ts>\n+constexpr bool check_hash_size = hash_size<Ts...> == Size;\n+#endif\n+\n+static_assert( check_hash_size<1, int> );\n+static_assert( check_hash_size<1, int, long, double> );\n+static_assert( check_hash_size<2, int, long, int> );\n+static_assert( check_hash_size<2, int, long, const int> );\n+static_assert( check_hash_size<3, int, int, const int> );\n+\n+static_assert( check_hash_size<1, S> );\n+static_assert( check_hash_size<1, int, S> );\n+static_assert( check_hash_size<2, int, S, int> );\n+static_assert( check_hash_size<2, int, S, int, S> );\n+static_assert( check_hash_size<2, int, S, S, int> );\n+static_assert( check_hash_size<3, int, S, S, int, S> );\n+\n+// For the default ABI this has two __hash_empty_base<int> base classes,\n+// for the unstable ABI it does not.\n+struct H\n+: std::hash<std::variant<int>>, std::hash<std::variant<long, int>>\n+{ };\n+static_assert( sizeof(H) == hash_size<int, int, long> );\n+// Likewise, even though one of the base classes is a disabled specialization.\n+struct HX\n+: std::hash<std::variant<int>>, std::hash<std::variant<S, int>>\n+{ };\n+static_assert( sizeof(HX) == hash_size<int, S, int> );\n", "prefixes": [ "v1", "1/1" ] }