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 <cassert>
14 #include <cstddef>
15 #include <type_traits>
16 #include <utility>
17
18 #include "test_macros.h"
19 #include "test_workarounds.h"
20
21 #if TEST_STD_VER < 11
22 #error this header may only be used in C++11 or newer
23 #endif
24
25 template <class ...Args> struct TypeList;
26
27 // Test that the specified Hash meets the requirements of an enabled hash
28 template <class Hash, class Key, class InputKey = Key>
29 TEST_CONSTEXPR_CXX20 void test_hash_enabled(InputKey const& key = InputKey{});
30
31 template <class T, class InputKey = T>
32 TEST_CONSTEXPR_CXX20 void test_hash_enabled_for_type(InputKey const& key = InputKey{}) {
33 return test_hash_enabled<std::hash<T>, T, InputKey>(key);
34 }
35
36 // Test that the specified Hash meets the requirements of a disabled hash.
37 template <class Hash, class Key>
38 void test_hash_disabled();
39
40 template <class T>
test_hash_disabled_for_type()41 void test_hash_disabled_for_type() {
42 return test_hash_disabled<std::hash<T>, T>();
43 }
44
45 namespace PoisonedHashDetail {
46 enum Enum {};
47 enum EnumClass : bool {};
48 struct Class {};
49 }
50
51 // Each header that declares the template hash provides enabled
52 // specializations of hash for nullptr t and all cv-unqualified
53 // arithmetic, enumeration, and pointer types.
54 using LibraryHashTypes = TypeList<
55 #if TEST_STD_VER > 14
56 decltype(nullptr),
57 #endif
58 bool,
59 char,
60 signed char,
61 unsigned char,
62 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
63 wchar_t,
64 #endif
65 char16_t,
66 char32_t,
67 short,
68 unsigned short,
69 int,
70 unsigned int,
71 long,
72 unsigned long,
73 long long,
74 unsigned long long,
75 #ifndef TEST_HAS_NO_INT128
76 __int128_t,
77 __uint128_t,
78 #endif
79 float,
80 double,
81 long double,
82 PoisonedHashDetail::Enum,
83 PoisonedHashDetail::EnumClass,
84 void*,
85 void const*,
86 PoisonedHashDetail::Class*
87 >;
88
89
90 // Test that each of the library hash specializations for arithmetic types,
91 // enum types, and pointer types are available and enabled.
92 template <class Types = LibraryHashTypes>
93 void test_library_hash_specializations_available(Types = Types{});
94
95
96 namespace PoisonedHashDetail {
97
98 template <class T, class = typename T::foo_bar_baz>
instantiate(int)99 constexpr bool instantiate(int) { return true; }
instantiate(long)100 template <class> constexpr bool instantiate(long) { return true; }
instantiate()101 template <class T> constexpr bool instantiate() { return instantiate<T>(0); }
102
103 template <class To>
104 struct ConvertibleToSimple {
ToConvertibleToSimple105 operator To() const {
106 return To{};
107 }
108 };
109
110 template <class To>
111 struct ConvertibleTo {
112 To to{};
113 operator To&() & { return to; }
114 operator To const&() const & { return to; }
115 operator To&&() && { return std::move(to); }
116 operator To const&&() const && { return std::move(to); }
117 };
118
119 template <class Hasher, class Key, class Res = decltype(std::declval<Hasher&>()(std::declval<Key>()))>
can_hash(int)120 constexpr bool can_hash(int) {
121 return std::is_same<Res, std::size_t>::value;
122 }
123 template <class, class>
can_hash(long)124 constexpr bool can_hash(long) {
125 return false;
126 }
127 template <class Hasher, class Key>
can_hash()128 constexpr bool can_hash() {
129 return can_hash<Hasher, Key>(0);
130 }
131 } // namespace PoisonedHashDetail
132
133 template <class Hash, class Key, class InputKey>
test_hash_enabled(InputKey const & key)134 TEST_CONSTEXPR_CXX20 void test_hash_enabled(InputKey const& key) {
135 using namespace PoisonedHashDetail;
136
137 static_assert(std::is_destructible<Hash>::value, "");
138 // Enabled hash requirements
139 static_assert(std::is_default_constructible<Hash>::value, "");
140 static_assert(std::is_copy_constructible<Hash>::value, "");
141 static_assert(std::is_move_constructible<Hash>::value, "");
142 static_assert(std::is_copy_assignable<Hash>::value, "");
143 static_assert(std::is_move_assignable<Hash>::value, "");
144
145 #if TEST_STD_VER > 14
146 static_assert(std::is_swappable<Hash>::value, "");
147 #elif defined(_LIBCPP_VERSION)
148 static_assert(std::__is_swappable<Hash>::value, "");
149 #endif
150
151 // Hashable requirements
152 static_assert(can_hash<Hash, Key&>(), "");
153 static_assert(can_hash<Hash, Key const&>(), "");
154 static_assert(can_hash<Hash, Key&&>(), "");
155 static_assert(can_hash<Hash const, Key&>(), "");
156 static_assert(can_hash<Hash const, Key const&>(), "");
157 static_assert(can_hash<Hash const, Key&&>(), "");
158
159 static_assert(can_hash<Hash, ConvertibleToSimple<Key>&>(), "");
160 static_assert(can_hash<Hash, ConvertibleToSimple<Key> const&>(), "");
161 static_assert(can_hash<Hash, ConvertibleToSimple<Key>&&>(), "");
162
163 static_assert(can_hash<Hash, ConvertibleTo<Key>&>(), "");
164 static_assert(can_hash<Hash, ConvertibleTo<Key> const&>(), "");
165 static_assert(can_hash<Hash, ConvertibleTo<Key>&&>(), "");
166 static_assert(can_hash<Hash, ConvertibleTo<Key> const&&>(), "");
167
168 const Hash h{};
169 assert(h(key) == h(key));
170
171 }
172
173 template <class Hash, class Key>
test_hash_disabled()174 void test_hash_disabled() {
175 using namespace PoisonedHashDetail;
176
177 // Disabled hash requirements
178 static_assert(!std::is_default_constructible<Hash>::value, "");
179 static_assert(!std::is_copy_constructible<Hash>::value, "");
180 static_assert(!std::is_move_constructible<Hash>::value, "");
181 static_assert(!std::is_copy_assignable<Hash>::value, "");
182 static_assert(!std::is_move_assignable<Hash>::value, "");
183
184 static_assert(!std::is_function<
185 typename std::remove_pointer<
186 typename std::remove_reference<Hash>::type
187 >::type
188 >::value, "");
189
190 // Hashable requirements
191 static_assert(!can_hash<Hash, Key&>(), "");
192 static_assert(!can_hash<Hash, Key const&>(), "");
193 static_assert(!can_hash<Hash, Key&&>(), "");
194 static_assert(!can_hash<Hash const, Key&>(), "");
195 static_assert(!can_hash<Hash const, Key const&>(), "");
196 static_assert(!can_hash<Hash const, Key&&>(), "");
197
198 static_assert(!can_hash<Hash, ConvertibleToSimple<Key>&>(), "");
199 static_assert(!can_hash<Hash, ConvertibleToSimple<Key> const&>(), "");
200 static_assert(!can_hash<Hash, ConvertibleToSimple<Key>&&>(), "");
201
202 static_assert(!can_hash<Hash, ConvertibleTo<Key>&>(), "");
203 static_assert(!can_hash<Hash, ConvertibleTo<Key> const&>(), "");
204 static_assert(!can_hash<Hash, ConvertibleTo<Key>&&>(), "");
205 static_assert(!can_hash<Hash, ConvertibleTo<Key> const&&>(), "");
206 }
207
208
209 template <class First, class ...Rest>
210 struct TypeList<First, Rest...> {
211 template <template <class> class Trait, bool Expect = true>
212 static constexpr bool assertTrait() {
213 static_assert(Trait<First>::value == Expect, "");
214 return TypeList<Rest...>::template assertTrait<Trait, Expect>();
215 }
216
217 template <class Trait>
218 static void applyTrait() {
219 Trait::template apply<First>();
220 TypeList<Rest...>::template applyTrait<Trait>();
221 }
222 };
223
224 template <>
225 struct TypeList<> {
226 template <template <class> class Trait, bool Expect = true>
227 static constexpr bool assertTrait() {
228 return true;
229 }
230 template <class Trait>
231 static void applyTrait() {}
232 };
233
234
235 struct TestLibraryTrait {
236 template <class Type>
237 static void apply() { test_hash_enabled<std::hash<Type>, Type>(); }
238 };
239
240 template <class Types>
241 void test_library_hash_specializations_available(Types) {
242 Types::template applyTrait<TestLibraryTrait >();
243 }
244
245 #endif // SUPPORT_POISONED_HASH_HELPER_H
246