blob: 824c35837b2b3e83c9055c91bc711a93fda6c7ff [file] [log] [blame]
Eric Fiselierf9127592017-01-21 00:02:12 +00001// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9//===----------------------------------------------------------------------===//
10#ifndef SUPPORT_POISONED_HASH_HELPER_HPP
11#define SUPPORT_POISONED_HASH_HELPER_HPP
12
13#include <type_traits>
14#include <cassert>
15
16#include "test_macros.h"
Eric Fiselier9506f142017-03-22 22:41:41 +000017#include "test_workarounds.h"
Eric Fiselierf9127592017-01-21 00:02:12 +000018
19#if TEST_STD_VER < 11
20#error this header may only be used in C++11 or newer
21#endif
22
23template <class ...Args> struct TypeList;
24
25// Test that the specified Hash meets the requirements of an enabled hash
26template <class Hash, class Key, class InputKey = Key>
27void test_hash_enabled(InputKey const& key = InputKey{});
28
29template <class T, class InputKey = T>
30void test_hash_enabled_for_type(InputKey const& key = InputKey{}) {
31 return test_hash_enabled<std::hash<T>, T, InputKey>(key);
32}
33
34// Test that the specified Hash meets the requirements of a disabled hash.
35template <class Hash, class Key>
36void test_hash_disabled();
37
38template <class T>
39void test_hash_disabled_for_type() {
40 return test_hash_disabled<std::hash<T>, T>();
41}
42
43namespace PoisonedHashDetail {
44 enum Enum {};
45 enum EnumClass : bool {};
46 struct Class {};
47}
48
49// Each header that declares the template hash provides enabled
50// specializations of hash for nullptr t and all cv-unqualified
51// arithmetic, enumeration, and pointer types.
52using LibraryHashTypes = TypeList<
Eric Fiselier9506f142017-03-22 22:41:41 +000053#if !defined(TEST_WORKAROUND_C1XX_BROKEN_NULLPTR_CONVERSION_OPERATOR)
Eric Fiselierf9127592017-01-21 00:02:12 +000054#if TEST_STD_VER > 14
55 decltype(nullptr),
56#endif
Eric Fiselier9506f142017-03-22 22:41:41 +000057#endif
Eric Fiselierf9127592017-01-21 00:02:12 +000058 bool,
59 char,
60 signed char,
61 unsigned char,
62 wchar_t,
63#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS
64 char16_t,
65 char32_t,
66#endif
67 short,
68 unsigned short,
69 int,
70 unsigned int,
71 long,
72 unsigned long,
73 long long,
74 unsigned long long,
75#ifndef _LIBCPP_HAS_NO_INT128
76 __int128_t,
77 __uint128_t,
78#endif
79 float,
80 double,
81 long double,
82#if TEST_STD_VER >= 14
83 // Enum types
84 PoisonedHashDetail::Enum,
85 PoisonedHashDetail::EnumClass,
86#endif
87 // pointer types
88 void*,
89 void const*,
90 PoisonedHashDetail::Class*
91 >;
92
93
94// Test that each of the library hash specializations for arithmetic types,
95// enum types, and pointer types are available and enabled.
96template <class Types = LibraryHashTypes>
97void test_library_hash_specializations_available(Types = Types{});
98
99
100namespace PoisonedHashDetail {
101
102template <class T, class = typename T::foo_bar_baz>
103constexpr bool instantiate(int) { return true; }
104template <class> constexpr bool instantiate(long) { return true; }
105template <class T> constexpr bool instantiate() { return instantiate<T>(0); }
106
107template <class To>
108struct ConvertibleToSimple {
109 operator To() const {
110 return To{};
111 }
112};
113
114template <class To>
115struct ConvertibleTo {
116 To to{};
117 operator To&() & { return to; }
118 operator To const&() const & { return to; }
119 operator To&&() && { return std::move(to); }
120 operator To const&&() const && { return std::move(to); }
121};
122
123template <class HashExpr,
124 class Res = typename std::result_of<HashExpr>::type>
125constexpr bool can_hash(int) {
126 return std::is_same<Res, size_t>::value;
127}
128template <class> constexpr bool can_hash(long) { return false; }
129template <class T> constexpr bool can_hash() { return can_hash<T>(0); }
130
131} // namespace PoisonedHashDetail
132
133template <class Hash, class Key, class InputKey>
134void 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 using CKey = ConvertibleTo<Key>;
153 static_assert(can_hash<Hash(Key&)>(), "");
154 static_assert(can_hash<Hash(Key const&)>(), "");
155 static_assert(can_hash<Hash(Key&&)>(), "");
156 static_assert(can_hash<Hash const&(Key&)>(), "");
157 static_assert(can_hash<Hash const&(Key const&)>(), "");
158 static_assert(can_hash<Hash const&(Key&&)>(), "");
159
160 static_assert(can_hash<Hash(ConvertibleToSimple<Key>&)>(), "");
161 static_assert(can_hash<Hash(ConvertibleToSimple<Key> const&)>(), "");
162 static_assert(can_hash<Hash(ConvertibleToSimple<Key>&&)>(), "");
163
164 static_assert(can_hash<Hash(ConvertibleTo<Key>&)>(), "");
165 static_assert(can_hash<Hash(ConvertibleTo<Key> const&)>(), "");
166 static_assert(can_hash<Hash(ConvertibleTo<Key> &&)>(), "");
167 static_assert(can_hash<Hash(ConvertibleTo<Key> const&&)>(), "");
168
Eric Fiselierd9e16312017-01-21 00:57:29 +0000169 const Hash h{};
Eric Fiselierf9127592017-01-21 00:02:12 +0000170 assert(h(key) == h(key));
171
172}
173
174template <class Hash, class Key>
175void test_hash_disabled() {
176 using namespace PoisonedHashDetail;
177
178 // Disabled hash requirements
179 static_assert(!std::is_default_constructible<Hash>::value, "");
180 static_assert(!std::is_copy_constructible<Hash>::value, "");
181 static_assert(!std::is_move_constructible<Hash>::value, "");
182 static_assert(!std::is_copy_assignable<Hash>::value, "");
183 static_assert(!std::is_move_assignable<Hash>::value, "");
184
185 static_assert(!std::is_function<
186 typename std::remove_pointer<
187 typename std::remove_reference<Hash>::type
188 >::type
189 >::value, "");
190
191 // Hashable requirements
192 using CKey = ConvertibleTo<Key>;
193 static_assert(!can_hash<Hash(Key&)>(), "");
194 static_assert(!can_hash<Hash(Key const&)>(), "");
195 static_assert(!can_hash<Hash(Key&&)>(), "");
196 static_assert(!can_hash<Hash const&(Key&)>(), "");
197 static_assert(!can_hash<Hash const&(Key const&)>(), "");
198 static_assert(!can_hash<Hash const&(Key&&)>(), "");
199
200 static_assert(!can_hash<Hash(ConvertibleToSimple<Key>&)>(), "");
201 static_assert(!can_hash<Hash(ConvertibleToSimple<Key> const&)>(), "");
202 static_assert(!can_hash<Hash(ConvertibleToSimple<Key>&&)>(), "");
203
204 static_assert(!can_hash<Hash(ConvertibleTo<Key>&)>(), "");
205 static_assert(!can_hash<Hash(ConvertibleTo<Key> const&)>(), "");
206 static_assert(!can_hash<Hash(ConvertibleTo<Key> &&)>(), "");
207 static_assert(!can_hash<Hash(ConvertibleTo<Key> const&&)>(), "");
208}
209
210
211template <class First, class ...Rest>
212struct TypeList<First, Rest...> {
213 template <template <class> class Trait, bool Expect = true>
214 static constexpr bool assertTrait() {
215 static_assert(Trait<First>::value == Expect, "");
216 return TypeList<Rest...>::template assertTrait<Trait, Expect>();
217 }
218
219 template <class Trait>
220 static void applyTrait() {
221 Trait::template apply<First>();
222 TypeList<Rest...>::template applyTrait<Trait>();
223 }
224};
225
226template <>
227struct TypeList<> {
228 template <template <class> class Trait, bool Expect = true>
229 static constexpr bool assertTrait() {
230 return true;
231 }
232 template <class Trait>
233 static void applyTrait() {}
234};
235
236
237struct TestLibraryTrait {
238 template <class Type>
239 static void apply() { test_hash_enabled<std::hash<Type>, Type>(); }
240};
241
242template <class Types>
243void test_library_hash_specializations_available(Types) {
244 Types::template applyTrait<TestLibraryTrait >();
245}
246
247#endif // SUPPORT_POISONED_HASH_HELPER_HPP