Home | History | Annotate | Download | only in support
      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