Eric Fiselier | f912759 | 2017-01-21 00:02:12 +0000 | [diff] [blame] | 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" |
Eric Fiselier | 9506f14 | 2017-03-22 22:41:41 +0000 | [diff] [blame] | 17 | #include "test_workarounds.h" |
Eric Fiselier | f912759 | 2017-01-21 00:02:12 +0000 | [diff] [blame] | 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 | |
Eric Fiselier | d9e1631 | 2017-01-21 00:57:29 +0000 | [diff] [blame] | 167 | const Hash h{}; |
Eric Fiselier | f912759 | 2017-01-21 00:02:12 +0000 | [diff] [blame] | 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 |