From patchwork Wed Nov 6 15:48:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 2007608 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=BEFodKDO; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Xk8mB4yQbz1xyW for ; Thu, 7 Nov 2024 02:50:01 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 25DD83858403 for ; Wed, 6 Nov 2024 15:49:59 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id B5F113858D21 for ; Wed, 6 Nov 2024 15:49:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B5F113858D21 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B5F113858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730908167; cv=none; b=fuqElF+tpFBd9hY5lJpMERgt+bolUgdoBcPolt8wZBEU+/VcVneHrh+cB+6Fw8QulZzLSxPoKNj+hebnywXK7NyQdaHDW/rP0/rbT6R05Ca5LhOmIi8U1kjhEIgtcXsJEQBbCL3ED9DDN8RHyJZa1FoNnZ8eSkN14bbB8NmGloU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730908167; c=relaxed/simple; bh=roCllVT1yhGite1V4HpNUrMI5drnOKP2cpVG5vAlfYE=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=MsgDYaZROeHZmEYU+wuL9roSmv/5FLhVC95pUo4UdxwGSE7+lFfXyKvNXc/GH+68Ef0RH0dDMIfNB2V9g/nx834e4E+w4fmyY6Kb+5jAzZGPjGXISbbXuhPOcUyFNtXdRqcZ5BPE/M4jpbv1pyyRHElST6CSHZ8I2IQo5vptrt8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1730908163; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=/n/I1P9EKLwKUnkwKQs7eqEPJRJ24hTrs3wnsDndKSQ=; b=BEFodKDOrhPfxxyXbYDnpvtDc/nCeXjgIprI0UT12qaKCxDpqiCMExgJbaS47lPmKGlBXn MnCB/y3Fz2z/AJvjL9DVKmBlcLGx7QacTvcV694OKw+Hq6eq1jYYkDbgD0tVR1I6mhLcPs QJOuq7No+2D0MU4g3NyRimqZbEZMTg8= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-180-zDq4DSH6O86KQ8kYKlstlA-1; Wed, 06 Nov 2024 10:49:20 -0500 X-MC-Unique: zDq4DSH6O86KQ8kYKlstlA-1 X-Mimecast-MFC-AGG-ID: zDq4DSH6O86KQ8kYKlstlA Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 625B51955D42; Wed, 6 Nov 2024 15:49:19 +0000 (UTC) Received: from localhost (unknown [10.42.28.185]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CD917300018D; Wed, 6 Nov 2024 15:49:18 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++: Refactor std::hash specializations Date: Wed, 6 Nov 2024 15:48:17 +0000 Message-ID: <20241106154917.379971-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: qasmcYLC2vPfDR84HQDw4blgoEmbTiUpAKslTFDgSF8_1730908159 X-Mimecast-Originator: redhat.com X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org 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. --- Tested x86_64-linux. Also available for review at: https://forge.sourceware.org/gcc/gcc-TEST/pulls/16 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.

\ No newline at end of file diff --git a/libstdc++-v3/doc/xml/manual/evolution.xml b/libstdc++-v3/doc/xml/manual/evolution.xml index 6b134de0e71..84f8ab9d298 100644 --- a/libstdc++-v3/doc/xml/manual/evolution.xml +++ b/libstdc++-v3/doc/xml/manual/evolution.xml @@ -1145,6 +1145,11 @@ and removed in C++20: <ctgmath>. + +Nested result_type and argument_type removed from +std::hash specializations for C++20. + + diff --git a/libstdc++-v3/include/bits/functional_hash.h b/libstdc++-v3/include/bits/functional_hash.h index e7d8c6c2054..6815edeb833 100644 --- a/libstdc++-v3/include/bits/functional_hash.h +++ b/libstdc++-v3/include/bits/functional_hash.h @@ -52,43 +52,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template struct __hash_base { +#if __cplusplus < 202002L typedef _Result result_type _GLIBCXX17_DEPRECATED; typedef _Arg argument_type _GLIBCXX17_DEPRECATED; +#endif }; +#if ! _GLIBCXX_INLINE_VERSION + // Some std::hash specializations inherit this for ABI compatibility reasons. + template struct __hash_empty_base { }; +#endif + /// Primary class template hash. template struct hash; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++14-extensions" template - struct __poison_hash - { - static constexpr bool __enable_hash_call = false; - private: - // Private rather than deleted to be non-trivially-copyable. - __poison_hash(__poison_hash&&); - ~__poison_hash(); - }; + constexpr bool __is_hash_enabled_for = false; template - struct __poison_hash<_Tp, __void_t()(declval<_Tp>()))>> - { - static constexpr bool __enable_hash_call = true; - }; + constexpr bool + __is_hash_enabled_for<_Tp, + __void_t()(declval<_Tp>()))>> + = true; +#pragma GCC diagnostic pop - // Helper struct for SFINAE-poisoning non-enum types. - template::value> - struct __hash_enum + // Helper struct for defining disabled specializations of std::hash. + template + struct __hash_not_enabled { - private: - // Private rather than deleted to be non-trivially-copyable. - __hash_enum(__hash_enum&&); - ~__hash_enum(); + __hash_not_enabled(__hash_not_enabled&&) = delete; + ~__hash_not_enabled() = delete; }; // Helper struct for hash with enum types. - template - struct __hash_enum<_Tp, true> : public __hash_base + template + struct __hash_enum : public __hash_base { size_t operator()(_Tp __val) const noexcept @@ -99,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; /// Primary class template hash, usable for enum types only. - // Use with non-enum types still SFINAES. template - struct hash : __hash_enum<_Tp> + struct hash + : __conditional_t<__is_enum(_Tp), __hash_enum<_Tp>, __hash_not_enabled<_Tp>> { }; /// Partial specializations for pointer types. diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 182173aa857..cf24ba80a61 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -1012,11 +1012,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @} relates unique_ptr /// @cond undocumented - template::__enable_hash_call> + template struct __uniq_ptr_hash + : public __hash_base #if ! _GLIBCXX_INLINE_VERSION - : private __poison_hash<_Ptr> + , private __hash_empty_base<_Ptr> #endif { size_t @@ -1025,17 +1025,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return hash<_Ptr>()(__u.get()); } }; - template - struct __uniq_ptr_hash<_Up, _Ptr, false> - : private __poison_hash<_Ptr> - { }; + template + using __uniq_ptr_hash_base + = __conditional_t<__is_hash_enabled_for, + __uniq_ptr_hash<_Up>, + __hash_not_enabled>; /// @endcond /// std::hash specialization for unique_ptr. template struct hash> - : public __hash_base>, - public __uniq_ptr_hash> + : public __uniq_ptr_hash_base> { }; #ifdef __glibcxx_make_unique // C++ >= 14 && HOSTED diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 2e663d13f86..b8eedeec781 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -1732,10 +1732,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Hash. - template, - bool = __poison_hash<_Up>::__enable_hash_call> - struct __optional_hash_call_base + template> + struct __optional_hash +#if ! _GLIBCXX_INLINE_VERSION + : public __hash_empty_base<_Up> +#endif { +#if __cplusplus < 202002L + using result_type [[__deprecated__]] = size_t; + using argument_type [[__deprecated__]] = optional<_Tp>; +#endif + size_t operator()(const optional<_Tp>& __t) const noexcept(noexcept(hash<_Up>{}(*__t))) @@ -1747,17 +1754,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; - template - struct __optional_hash_call_base<_Tp, _Up, false> {}; - template struct hash> - : private __poison_hash>, - public __optional_hash_call_base<_Tp> - { - using result_type [[__deprecated__]] = size_t; - using argument_type [[__deprecated__]] = optional<_Tp>; - }; + : public __conditional_t<__is_hash_enabled_for>, + __optional_hash<_Tp>, + __hash_not_enabled<_Tp>> + { }; template struct __is_fast_hash>> : __is_fast_hash> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index bd0f9c3252a..32e53998083 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -1094,7 +1094,8 @@ namespace __variant = __gen_vtable_impl<_Array_type, std::index_sequence<>>::_S_apply(); }; - template +#if ! _GLIBCXX_INLINE_VERSION + template struct _Base_dedup : public _Tp { }; template @@ -1103,7 +1104,9 @@ namespace __variant template struct _Variant_hash_base, std::index_sequence<__indices...>> - : _Base_dedup<__indices, __poison_hash>>... { }; + : _Base_dedup<__indices, __hash_empty_base>>... + { }; +#endif // Equivalent to decltype(get<_Np>(as-variant(declval<_Variant>()))) template - struct __variant_hash_call_base_impl + template + struct __variant_hash { +#if __cplusplus < 202002L + using result_type [[__deprecated__]] = size_t; + using argument_type [[__deprecated__]] = variant<_Types...>; +#endif + size_t operator()(const variant<_Types...>& __t) const noexcept((is_nothrow_invocable_v>, _Types> && ...)) @@ -2009,31 +2017,26 @@ namespace __detail::__variant return __ret; } }; - - template - struct __variant_hash_call_base_impl {}; - - template - using __variant_hash_call_base = - __variant_hash_call_base_impl<(__poison_hash>:: - __enable_hash_call &&...), _Types...>; /// @endcond template struct hash> - : private __detail::__variant::_Variant_hash_base< - variant<_Types...>, std::index_sequence_for<_Types...>>, - public __variant_hash_call_base<_Types...> - { - using result_type [[__deprecated__]] = size_t; - using argument_type [[__deprecated__]] = variant<_Types...>; - }; + : __conditional_t<(__is_hash_enabled_for> && ...), + __variant_hash<_Types...>, + __hash_not_enabled>> +#if ! _GLIBCXX_INLINE_VERSION + , __detail::__variant::_Variant_hash_base, + index_sequence_for<_Types...>> +#endif + { }; template<> struct hash { +#if __cplusplus < 202002L using result_type [[__deprecated__]] = size_t; using argument_type [[__deprecated__]] = monostate; +#endif size_t operator()(const monostate&) const noexcept diff --git a/libstdc++-v3/testsuite/20_util/optional/hash.cc b/libstdc++-v3/testsuite/20_util/optional/hash.cc index e441c87e0c9..607f56459ab 100644 --- a/libstdc++-v3/testsuite/20_util/optional/hash.cc +++ b/libstdc++-v3/testsuite/20_util/optional/hash.cc @@ -49,3 +49,36 @@ int main() std::optional x3 = x2; VERIFY(std::hash()(x) == std::hash>()(x3)); } + +// Check for presence/absence of nested types. + +template using res_type = typename std::hash::result_type; +template using arg_type = typename std::hash::argument_type; + +template +constexpr bool has_res_type = false; +template +constexpr bool has_res_type>> = true; +template +constexpr bool has_arg_type = false; +template +constexpr bool has_arg_type>> = true; + +template +constexpr bool has_no_types + = ! has_res_type> && ! has_arg_type>; + +#if __cplusplus >= 202002L +// Nested types result_type and argument_type are not present in C++20 +static_assert( has_no_types ); +static_assert( has_no_types ); +#else +// Nested types result_type and argument_type are deprecated in C++17. +using R1 = std::hash>::result_type; // { dg-warning "deprecated" "" { target c++17_only } } +using A1 = std::hash>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } } +using R2 = std::hash>::result_type; // { dg-warning "deprecated" "" { target c++17_only } } +using A2 = std::hash>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } } +#endif + +// Disabled specializations do not have the nested types. +static_assert( has_no_types ); diff --git a/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc b/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc new file mode 100644 index 00000000000..78e992c4e41 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/hash_abi.cc @@ -0,0 +1,35 @@ +// { dg-do compile { target c++17 } } + +#include + +struct S { }; // std::hash is a disabled specialization. + +template +constexpr std::size_t hash_size = sizeof(std::hash>); + +template +struct MultiHash : std::hash>... +{ }; + +#if _GLIBCXX_INLINE_VERSION +// For the unstable ABI the size should always be one. +template +constexpr bool check_hash_size = sizeof(MultiHash) == 1; +#else +// For the default ABI, each std::hash> specialization has +// a base class of type __hash_empty_base> and if +// the same type occurs more than once they must have unique addresses. +template +constexpr bool check_hash_size = sizeof(MultiHash) == Size; +#endif + +static_assert( check_hash_size<1, int> ); +static_assert( check_hash_size<1, int, long, double> ); +static_assert( check_hash_size<2, int, const int> ); +static_assert( check_hash_size<2, int, long, const int> ); + +static_assert( check_hash_size<1, S> ); +static_assert( check_hash_size<1, int, S> ); +static_assert( check_hash_size<2, int, S, const int> ); +static_assert( check_hash_size<2, int, S, const int, const S> ); +static_assert( check_hash_size<2, int, S, const S, const int> ); diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc new file mode 100644 index 00000000000..9c0a32c49c9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/abi.cc @@ -0,0 +1,64 @@ +// { dg-do compile { target c++17 } } + +#include + +struct S { }; // std::hash is a disabled specialization. + +template> +constexpr std::size_t hash_size = sizeof(std::hash>); + +template +struct MultiHash : std::hash>... +{ }; + +#if _GLIBCXX_INLINE_VERSION +// For the unstable ABI the size should always be one. +template +constexpr bool check_hash_size = sizeof(MultiHash) == 1; +#else +// For the default ABI, each std::hash> specialization +// has a base class of type __hash_empty_base and if +// the same type occurs more than once they must have unique addresses. +template +constexpr bool check_hash_size = sizeof(MultiHash) == Size; +#endif + +// All these types have distinct D::pointer types, so no duplicate base classes +static_assert( check_hash_size<1, int>, "" ); +static_assert( check_hash_size<1, int, long, double>, "" ); +static_assert( check_hash_size<1, int, const int>, "" ); +static_assert( check_hash_size<1, int, long, const int>, "" ); +// Likewise for these disabled specializations: +static_assert( check_hash_size<1, S>, "" ); +static_assert( check_hash_size<1, int, S>, "" ); +static_assert( check_hash_size<1, int, S, const int>, "" ); +static_assert( check_hash_size<1, int, S, const int, const S>, "" ); +static_assert( check_hash_size<1, int, S, const S, const int>, "" ); + +// But this has two base classes of type __hash_empty_base: +static_assert( check_hash_size<2, int, int[]>, "" ); +static_assert( check_hash_size<2, int, int[], const int>, "" ); +// And this has two base classes of type __hash_not_enabled: +static_assert( check_hash_size<2, S, S[]>, "" ); +static_assert( check_hash_size<2, S, S[], const S>, "" ); + +struct Del : std::default_delete { }; +using P = std::unique_ptr; +using PD = std::unique_ptr; +using PC = std::unique_ptr>; +using PA = std::unique_ptr; +struct HashClash +: std::hash

, std::hash, std::hash, std::hash +{ }; +#if _GLIBCXX_INLINE_VERSION +static_assert(sizeof(HashClash) == 1, "No duplicate bases for unstable ABI"); +#else +static_assert(sizeof(HashClash) == 4, "four __hash_empty_base bases"); +#endif + +struct Del2 : std::default_delete { using pointer = const int*; }; +using PD2 = std::unique_ptr; +struct Hash2 +: std::hash, std::hash +{ }; +static_assert(sizeof(Hash2) == 1, "No duplicate bases"); diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc new file mode 100644 index 00000000000..dbe3d8084fe --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/types.cc @@ -0,0 +1,53 @@ +// { dg-do compile { target c++11 } } + +#include + + +// Check for presence/absence of nested types. + +template using res_type = typename std::hash::result_type; +template using arg_type = typename std::hash::argument_type; + +template +constexpr bool +has_res_type(...) +{ return false; } + +template +constexpr typename std::is_void>::value_type // i.e. bool +has_res_type() +{ return true; } + +template +constexpr bool +has_arg_type(...) +{ return false; } + +template +constexpr typename std::is_void>::value_type // i.e. bool +has_arg_type() +{ return true; } + +template +constexpr bool +has_no_types() +{ return ! has_res_type() && ! has_arg_type(); } + +#if __cplusplus >= 202002L +// Nested types result_type and argument_type are not present in C++20 +static_assert( has_no_types>() ); +static_assert( has_no_types>() ); +#else +// Nested types result_type and argument_type are deprecated in C++17. +using R1 = std::hash>::result_type; // { dg-warning "deprecated" "" { target c++17_only } } +using A1 = std::hash>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } } +#endif + +struct S { }; +template<> struct std::hash // disabled specialization +{ + hash(hash&&) = delete; + ~hash() = delete; +}; +// Disabled specializations do not have the nested types. +static_assert( has_no_types>(), "disabled specialization" ); diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc index f3ab4e0d9ca..52fc759fc34 100644 --- a/libstdc++-v3/testsuite/20_util/variant/hash.cc +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -48,3 +48,37 @@ int main() std::variant x2 = 42; VERIFY(std::hash()(x) == std::hash>()(x2)); } + +// Check for presence/absence of nested types. + +template using res_type = typename std::hash::result_type; +template using arg_type = typename std::hash::argument_type; + +template +constexpr bool has_res_type = false; +template +constexpr bool has_res_type>> = true; +template +constexpr bool has_arg_type = false; +template +constexpr bool has_arg_type>> = true; + +template +constexpr bool has_no_types + = ! has_res_type> && ! has_arg_type>; + +#if __cplusplus >= 202002L +// Nested types result_type and argument_type are not present in C++20 +static_assert( has_no_types ); +static_assert( has_no_types ); +#else +// Nested types result_type and argument_type are deprecated in C++17. +using R1 = std::hash>::result_type; // { dg-warning "deprecated" "" { target c++17_only } } +using A1 = std::hash>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } } +using R2 = std::hash>::result_type; // { dg-warning "deprecated" "" { target c++17_only } } +using A2 = std::hash>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } } +#endif + +// Disabled specializations do not have the nested types. +static_assert( has_no_types ); +static_assert( has_no_types ); diff --git a/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc b/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc new file mode 100644 index 00000000000..19cf72b54de --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/hash_abi.cc @@ -0,0 +1,48 @@ +// { dg-options "-Wdeprecated" } +// { dg-do compile { target c++17 } } + +#include + +struct S { }; // std::hash is a disabled specialization. + +// Test std::hash size + +template +constexpr std::size_t hash_size = sizeof(std::hash>); + +#if _GLIBCXX_INLINE_VERSION +// For the unstable ABI the size should always be one. +template +constexpr bool check_hash_size = hash_size == 1; +#else +// For the default ABI, the std::hash specialization has sizeof...(Ts) +// base classes of types __hash_empty_base>... and if +// the same type occurs more than once they must have unique addresses. +template +constexpr bool check_hash_size = hash_size == Size; +#endif + +static_assert( check_hash_size<1, int> ); +static_assert( check_hash_size<1, int, long, double> ); +static_assert( check_hash_size<2, int, long, int> ); +static_assert( check_hash_size<2, int, long, const int> ); +static_assert( check_hash_size<3, int, int, const int> ); + +static_assert( check_hash_size<1, S> ); +static_assert( check_hash_size<1, int, S> ); +static_assert( check_hash_size<2, int, S, int> ); +static_assert( check_hash_size<2, int, S, int, S> ); +static_assert( check_hash_size<2, int, S, S, int> ); +static_assert( check_hash_size<3, int, S, S, int, S> ); + +// For the default ABI this has two __hash_empty_base base classes, +// for the unstable ABI it does not. +struct H +: std::hash>, std::hash> +{ }; +static_assert( sizeof(H) == hash_size ); +// Likewise, even though one of the base classes is a disabled specialization. +struct HX +: std::hash>, std::hash> +{ }; +static_assert( sizeof(HX) == hash_size );