xref: /llvm-project/libcxx/test/support/poisoned_hash_helper.h (revision 33325524f5e80a898a7ae875e208a54af132001b)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef SUPPORT_POISONED_HASH_HELPER_H
11 #define SUPPORT_POISONED_HASH_HELPER_H
12 
13 #include <functional>
14 #include <cassert>
15 #include <cstddef>
16 #include <type_traits>
17 #include <utility>
18 
19 #include "test_macros.h"
20 #include "type_algorithms.h"
21 
22 template <class Hash, class Key, class Res = decltype(std::declval<Hash&>()(std::declval<Key>()))>
23 constexpr bool can_hash_impl(int) {
24   return std::is_same<Res, std::size_t>::value;
25 }
26 template <class, class>
27 constexpr bool can_hash_impl(long) {
28   return false;
29 }
30 template <class Hash, class Key>
31 constexpr bool can_hash() {
32   return can_hash_impl<Hash, Key>(0);
33 }
34 
35 template <class To>
36 struct ConvertibleToSimple {
37   operator To() const { return To{}; }
38 };
39 
40 template <class To>
41 struct ConvertibleTo {
42   To to{};
43   operator To&() & { return to; }
44   operator To const&() const& { return to; }
45   operator To&&() && { return std::move(to); }
46   operator To const&&() const&& { return std::move(to); }
47 };
48 
49 // Test that the specified Hash meets the requirements of an enabled hash
50 template <class Key, class Hash = std::hash<Key>>
51 TEST_CONSTEXPR_CXX20 void test_hash_enabled(Key const& key = Key{}) {
52   static_assert(std::is_destructible<Hash>::value, "");
53 
54   // Enabled hash requirements
55   static_assert(std::is_default_constructible<Hash>::value, "");
56   static_assert(std::is_copy_constructible<Hash>::value, "");
57   static_assert(std::is_move_constructible<Hash>::value, "");
58   static_assert(std::is_copy_assignable<Hash>::value, "");
59   static_assert(std::is_move_assignable<Hash>::value, "");
60 
61 #if TEST_STD_VER > 14
62   static_assert(std::is_swappable<Hash>::value, "");
63 #elif defined(_LIBCPP_VERSION)
64   static_assert(std::__is_swappable_v<Hash>, "");
65 #endif
66 
67   // Hashable requirements
68   static_assert(can_hash<Hash, Key&>(), "");
69   static_assert(can_hash<Hash, Key const&>(), "");
70   static_assert(can_hash<Hash, Key&&>(), "");
71   static_assert(can_hash<Hash const, Key&>(), "");
72   static_assert(can_hash<Hash const, Key const&>(), "");
73   static_assert(can_hash<Hash const, Key&&>(), "");
74 
75   static_assert(can_hash<Hash, ConvertibleToSimple<Key>&>(), "");
76   static_assert(can_hash<Hash, ConvertibleToSimple<Key> const&>(), "");
77   static_assert(can_hash<Hash, ConvertibleToSimple<Key>&&>(), "");
78 
79   static_assert(can_hash<Hash, ConvertibleTo<Key>&>(), "");
80   static_assert(can_hash<Hash, ConvertibleTo<Key> const&>(), "");
81   static_assert(can_hash<Hash, ConvertibleTo<Key>&&>(), "");
82   static_assert(can_hash<Hash, ConvertibleTo<Key> const&&>(), "");
83 
84   const Hash h{};
85   assert(h(key) == h(key));
86 }
87 
88 // Test that the specified Hash meets the requirements of a disabled hash.
89 template <class Key, class Hash = std::hash<Key>>
90 void test_hash_disabled() {
91   // Disabled hash requirements
92   static_assert(!std::is_default_constructible<Hash>::value, "");
93   static_assert(!std::is_copy_constructible<Hash>::value, "");
94   static_assert(!std::is_move_constructible<Hash>::value, "");
95   static_assert(!std::is_copy_assignable<Hash>::value, "");
96   static_assert(!std::is_move_assignable<Hash>::value, "");
97 
98   static_assert(
99       !std::is_function<typename std::remove_pointer<typename std::remove_reference<Hash>::type>::type>::value, "");
100 
101   // Hashable requirements
102   static_assert(!can_hash<Hash, Key&>(), "");
103   static_assert(!can_hash<Hash, Key const&>(), "");
104   static_assert(!can_hash<Hash, Key&&>(), "");
105   static_assert(!can_hash<Hash const, Key&>(), "");
106   static_assert(!can_hash<Hash const, Key const&>(), "");
107   static_assert(!can_hash<Hash const, Key&&>(), "");
108 
109   static_assert(!can_hash<Hash, ConvertibleToSimple<Key>&>(), "");
110   static_assert(!can_hash<Hash, ConvertibleToSimple<Key> const&>(), "");
111   static_assert(!can_hash<Hash, ConvertibleToSimple<Key>&&>(), "");
112 
113   static_assert(!can_hash<Hash, ConvertibleTo<Key>&>(), "");
114   static_assert(!can_hash<Hash, ConvertibleTo<Key> const&>(), "");
115   static_assert(!can_hash<Hash, ConvertibleTo<Key>&&>(), "");
116   static_assert(!can_hash<Hash, ConvertibleTo<Key> const&&>(), "");
117 }
118 
119 enum Enum {};
120 enum EnumClass : bool {};
121 struct Class {};
122 
123 // Each header that declares the std::hash template provides enabled
124 // specializations of std::hash for std::nullptr_t and all cv-unqualified
125 // arithmetic, enumeration, and pointer types.
126 #if TEST_STD_VER >= 17
127 using MaybeNullptr = types::type_list<std::nullptr_t>;
128 #else
129 using MaybeNullptr = types::type_list<>;
130 #endif
131 using LibraryHashTypes = types::
132     concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
133 
134 struct TestHashEnabled {
135   template <class T>
136   void operator()() const {
137     test_hash_enabled<T>();
138   }
139 };
140 
141 // Test that each of the library hash specializations for arithmetic types,
142 // enum types, and pointer types are available and enabled.
143 template <class Types = LibraryHashTypes>
144 void test_library_hash_specializations_available() {
145   types::for_each(Types(), TestHashEnabled());
146 }
147 
148 #endif // SUPPORT_POISONED_HASH_HELPER_H
149