1 // -*- 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" 17 #include "test_workarounds.h" 18 19 #if TEST_STD_VER < 11 20 #error this header may only be used in C++11 or newer 21 #endif 22 23 template <class ...Args> struct TypeList; 24 25 // Test that the specified Hash meets the requirements of an enabled hash 26 template <class Hash, class Key, class InputKey = Key> 27 void test_hash_enabled(InputKey const& key = InputKey{}); 28 29 template <class T, class InputKey = T> 30 void 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. 35 template <class Hash, class Key> 36 void test_hash_disabled(); 37 38 template <class T> 39 void test_hash_disabled_for_type() { 40 return test_hash_disabled<std::hash<T>, T>(); 41 } 42 43 namespace 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. 52 using LibraryHashTypes = TypeList< 53 #if TEST_STD_VER > 14 54 decltype(nullptr), 55 #endif 56 bool, 57 char, 58 signed char, 59 unsigned char, 60 wchar_t, 61 #ifndef _LIBCPP_HAS_NO_UNICODE_CHARS 62 char16_t, 63 char32_t, 64 #endif 65 short, 66 unsigned short, 67 int, 68 unsigned int, 69 long, 70 unsigned long, 71 long long, 72 unsigned long long, 73 #ifndef _LIBCPP_HAS_NO_INT128 74 __int128_t, 75 __uint128_t, 76 #endif 77 float, 78 double, 79 long double, 80 #if TEST_STD_VER >= 14 81 // Enum types 82 PoisonedHashDetail::Enum, 83 PoisonedHashDetail::EnumClass, 84 #endif 85 // pointer types 86 void*, 87 void const*, 88 PoisonedHashDetail::Class* 89 >; 90 91 92 // Test that each of the library hash specializations for arithmetic types, 93 // enum types, and pointer types are available and enabled. 94 template <class Types = LibraryHashTypes> 95 void test_library_hash_specializations_available(Types = Types{}); 96 97 98 namespace PoisonedHashDetail { 99 100 template <class T, class = typename T::foo_bar_baz> 101 constexpr bool instantiate(int) { return true; } 102 template <class> constexpr bool instantiate(long) { return true; } 103 template <class T> constexpr bool instantiate() { return instantiate<T>(0); } 104 105 template <class To> 106 struct ConvertibleToSimple { 107 operator To() const { 108 return To{}; 109 } 110 }; 111 112 template <class To> 113 struct ConvertibleTo { 114 To to{}; 115 operator To&() & { return to; } 116 operator To const&() const & { return to; } 117 operator To&&() && { return std::move(to); } 118 operator To const&&() const && { return std::move(to); } 119 }; 120 121 template <class HashExpr, 122 class Res = typename std::result_of<HashExpr>::type> 123 constexpr bool can_hash(int) { 124 return std::is_same<Res, size_t>::value; 125 } 126 template <class> constexpr bool can_hash(long) { return false; } 127 template <class T> constexpr bool can_hash() { return can_hash<T>(0); } 128 129 } // namespace PoisonedHashDetail 130 131 template <class Hash, class Key, class InputKey> 132 void test_hash_enabled(InputKey const& key) { 133 using namespace PoisonedHashDetail; 134 135 static_assert(std::is_destructible<Hash>::value, ""); 136 // Enabled hash requirements 137 static_assert(std::is_default_constructible<Hash>::value, ""); 138 static_assert(std::is_copy_constructible<Hash>::value, ""); 139 static_assert(std::is_move_constructible<Hash>::value, ""); 140 static_assert(std::is_copy_assignable<Hash>::value, ""); 141 static_assert(std::is_move_assignable<Hash>::value, ""); 142 143 #if TEST_STD_VER > 14 144 static_assert(std::is_swappable<Hash>::value, ""); 145 #elif defined(_LIBCPP_VERSION) 146 static_assert(std::__is_swappable<Hash>::value, ""); 147 #endif 148 149 // Hashable requirements 150 using CKey = ConvertibleTo<Key>; 151 static_assert(can_hash<Hash(Key&)>(), ""); 152 static_assert(can_hash<Hash(Key const&)>(), ""); 153 static_assert(can_hash<Hash(Key&&)>(), ""); 154 static_assert(can_hash<Hash const&(Key&)>(), ""); 155 static_assert(can_hash<Hash const&(Key const&)>(), ""); 156 static_assert(can_hash<Hash const&(Key&&)>(), ""); 157 158 static_assert(can_hash<Hash(ConvertibleToSimple<Key>&)>(), ""); 159 static_assert(can_hash<Hash(ConvertibleToSimple<Key> const&)>(), ""); 160 static_assert(can_hash<Hash(ConvertibleToSimple<Key>&&)>(), ""); 161 162 static_assert(can_hash<Hash(ConvertibleTo<Key>&)>(), ""); 163 static_assert(can_hash<Hash(ConvertibleTo<Key> const&)>(), ""); 164 static_assert(can_hash<Hash(ConvertibleTo<Key> &&)>(), ""); 165 static_assert(can_hash<Hash(ConvertibleTo<Key> const&&)>(), ""); 166 167 const Hash h{}; 168 assert(h(key) == h(key)); 169 170 } 171 172 template <class Hash, class Key> 173 void test_hash_disabled() { 174 using namespace PoisonedHashDetail; 175 176 // Disabled hash requirements 177 static_assert(!std::is_default_constructible<Hash>::value, ""); 178 static_assert(!std::is_copy_constructible<Hash>::value, ""); 179 static_assert(!std::is_move_constructible<Hash>::value, ""); 180 static_assert(!std::is_copy_assignable<Hash>::value, ""); 181 static_assert(!std::is_move_assignable<Hash>::value, ""); 182 183 static_assert(!std::is_function< 184 typename std::remove_pointer< 185 typename std::remove_reference<Hash>::type 186 >::type 187 >::value, ""); 188 189 // Hashable requirements 190 using CKey = ConvertibleTo<Key>; 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_HPP 246