From 509912a611f9e86883cd1f3c1751e4a1b3cb7abf Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Sat, 21 Jan 2017 17:38:23 +0200 Subject: [PATCH] Make poisoned hashes SFINAE away the call operator of the hash. * include/bits/functional_hash.h (__poison_hash::__enable_hash_call): New. * include/std/optional (__optional_hash_call_base): New. (hash>): Derive from the new base, move the hash function into that base. * include/std/variant (__variant_hash_call_base_impl): New. (__variant_hash_call_base): Likewise. (hash>): Derive from the new base, move the hash function into that base. * testsuite/20_util/optional/hash.cc: Add tests for is_callable. * testsuite/20_util/variant/hash.cc: Likewise. From-SVN: r244748 --- libstdc++-v3/ChangeLog | 15 +++++++++++ libstdc++-v3/include/bits/functional_hash.h | 2 ++ libstdc++-v3/include/std/optional | 21 +++++++++++---- libstdc++-v3/include/std/variant | 27 ++++++++++++++----- .../testsuite/20_util/optional/hash.cc | 6 +++++ .../testsuite/20_util/variant/hash.cc | 11 ++++++++ 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index dc60d4ee528..b96185a7393 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,18 @@ +2017-01-21 Ville Voutilainen + + Make poisoned hashes SFINAE away the call operator of the hash. + * include/bits/functional_hash.h + (__poison_hash::__enable_hash_call): New. + * include/std/optional (__optional_hash_call_base): New. + (hash>): Derive from the new base, + move the hash function into that base. + * include/std/variant (__variant_hash_call_base_impl): New. + (__variant_hash_call_base): Likewise. + (hash>): Derive from the new base, + move the hash function into that base. + * testsuite/20_util/optional/hash.cc: Add tests for is_callable. + * testsuite/20_util/variant/hash.cc: Likewise. + 2017-01-20 Joe Seymour * acinclude.m4 (GLIBCXX_CHECK_SIZE_T_MANGLING): Support uint20_t. diff --git a/libstdc++-v3/include/bits/functional_hash.h b/libstdc++-v3/include/bits/functional_hash.h index 14f7fae202f..38be1724d3f 100644 --- a/libstdc++-v3/include/bits/functional_hash.h +++ b/libstdc++-v3/include/bits/functional_hash.h @@ -60,6 +60,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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&&); @@ -69,6 +70,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template struct __poison_hash<_Tp, __void_t()(declval<_Tp>()))>> { + static constexpr bool __enable_hash_call = true; }; // Helper struct for SFINAE-poisoning non-enum types. diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 85ec91dbb95..887bf9e8397 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -951,12 +951,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return optional<_Tp> { in_place, __il, std::forward<_Args>(__args)... }; } // Hash. - template - struct hash> : private __poison_hash> - { - using result_type = size_t; - using argument_type = optional<_Tp>; + template>::__enable_hash_call> + struct __optional_hash_call_base + { size_t operator()(const optional<_Tp>& __t) const noexcept(noexcept(hash<_Tp> {}(*__t))) @@ -968,6 +967,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template + struct __optional_hash_call_base<_Tp, false> {}; + + template + struct hash> + : private __poison_hash>, + public __optional_hash_call_base<_Tp> + { + using result_type = size_t; + using argument_type = optional<_Tp>; + }; + /// @} _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 6404fceb02b..c5138e56803 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -1273,14 +1273,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_Variants>(__variants)...); } - template - struct hash> - : private __detail::__variant::_Variant_hash_base< - variant<_Types...>, std::index_sequence_for<_Types...>> + template + struct __variant_hash_call_base_impl { - using result_type = size_t; - using argument_type = variant<_Types...>; - size_t operator()(const variant<_Types...>& __t) const noexcept((is_nothrow_callable_v>(_Types)> && ...)) @@ -1297,6 +1292,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template + struct __variant_hash_call_base_impl {}; + + template + using __variant_hash_call_base = + __variant_hash_call_base_impl<(__poison_hash>:: + __enable_hash_call &&...), _Types...>; + + 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 = size_t; + using argument_type = variant<_Types...>; + }; + template<> struct hash { diff --git a/libstdc++-v3/testsuite/20_util/optional/hash.cc b/libstdc++-v3/testsuite/20_util/optional/hash.cc index ceb862b1b38..297ea2e276b 100644 --- a/libstdc++-v3/testsuite/20_util/optional/hash.cc +++ b/libstdc++-v3/testsuite/20_util/optional/hash.cc @@ -29,6 +29,12 @@ template auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f(0))::value, ""); +static_assert(!std::is_callable< + std::hash>& + (std::optional const&)>::value, ""); +static_assert(std::is_callable< + std::hash>& + (std::optional const&)>::value, ""); int main() { diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc index a9ebf33c24d..0a267ab68e4 100644 --- a/libstdc++-v3/testsuite/20_util/variant/hash.cc +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -33,6 +33,17 @@ static_assert(!decltype(f>(0))::value, ""); static_assert(!decltype(f>(0))::value, ""); static_assert(decltype(f>(0))::value, ""); static_assert(decltype(f>(0))::value, ""); +static_assert(!std::is_callable< + std::hash>&(std::variant const&)>::value, ""); +static_assert(!std::is_callable< + std::hash>& + (std::variant const&)>::value, ""); +static_assert(std::is_callable< + std::hash>& + (std::variant const&)>::value, ""); +static_assert(std::is_callable< + std::hash>& + (std::variant const&)>::value, ""); int main() { -- 2.30.2