diff options
Diffstat (limited to 'test/support/poisoned_hash_helper.hpp')
-rw-r--r-- | test/support/poisoned_hash_helper.hpp | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/test/support/poisoned_hash_helper.hpp b/test/support/poisoned_hash_helper.hpp new file mode 100644 index 0000000000000..824c35837b2b3 --- /dev/null +++ b/test/support/poisoned_hash_helper.hpp @@ -0,0 +1,247 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef SUPPORT_POISONED_HASH_HELPER_HPP +#define SUPPORT_POISONED_HASH_HELPER_HPP + +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_workarounds.h" + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 or newer +#endif + +template <class ...Args> struct TypeList; + +// Test that the specified Hash meets the requirements of an enabled hash +template <class Hash, class Key, class InputKey = Key> +void test_hash_enabled(InputKey const& key = InputKey{}); + +template <class T, class InputKey = T> +void test_hash_enabled_for_type(InputKey const& key = InputKey{}) { + return test_hash_enabled<std::hash<T>, T, InputKey>(key); +} + +// Test that the specified Hash meets the requirements of a disabled hash. +template <class Hash, class Key> +void test_hash_disabled(); + +template <class T> +void test_hash_disabled_for_type() { + return test_hash_disabled<std::hash<T>, T>(); +} + +namespace PoisonedHashDetail { + enum Enum {}; + enum EnumClass : bool {}; + struct Class {}; +} + +// Each header that declares the template hash provides enabled +// specializations of hash for nullptr t and all cv-unqualified +// arithmetic, enumeration, and pointer types. +using LibraryHashTypes = TypeList< +#if !defined(TEST_WORKAROUND_C1XX_BROKEN_NULLPTR_CONVERSION_OPERATOR) +#if TEST_STD_VER > 14 + decltype(nullptr), +#endif +#endif + bool, + char, + signed char, + unsigned char, + wchar_t, +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + char16_t, + char32_t, +#endif + short, + unsigned short, + int, + unsigned int, + long, + unsigned long, + long long, + unsigned long long, +#ifndef _LIBCPP_HAS_NO_INT128 + __int128_t, + __uint128_t, +#endif + float, + double, + long double, +#if TEST_STD_VER >= 14 + // Enum types + PoisonedHashDetail::Enum, + PoisonedHashDetail::EnumClass, +#endif + // pointer types + void*, + void const*, + PoisonedHashDetail::Class* + >; + + +// Test that each of the library hash specializations for arithmetic types, +// enum types, and pointer types are available and enabled. +template <class Types = LibraryHashTypes> +void test_library_hash_specializations_available(Types = Types{}); + + +namespace PoisonedHashDetail { + +template <class T, class = typename T::foo_bar_baz> +constexpr bool instantiate(int) { return true; } +template <class> constexpr bool instantiate(long) { return true; } +template <class T> constexpr bool instantiate() { return instantiate<T>(0); } + +template <class To> +struct ConvertibleToSimple { + operator To() const { + return To{}; + } +}; + +template <class To> +struct ConvertibleTo { + To to{}; + operator To&() & { return to; } + operator To const&() const & { return to; } + operator To&&() && { return std::move(to); } + operator To const&&() const && { return std::move(to); } +}; + +template <class HashExpr, + class Res = typename std::result_of<HashExpr>::type> +constexpr bool can_hash(int) { + return std::is_same<Res, size_t>::value; +} +template <class> constexpr bool can_hash(long) { return false; } +template <class T> constexpr bool can_hash() { return can_hash<T>(0); } + +} // namespace PoisonedHashDetail + +template <class Hash, class Key, class InputKey> +void test_hash_enabled(InputKey const& key) { + using namespace PoisonedHashDetail; + + static_assert(std::is_destructible<Hash>::value, ""); + // Enabled hash requirements + static_assert(std::is_default_constructible<Hash>::value, ""); + static_assert(std::is_copy_constructible<Hash>::value, ""); + static_assert(std::is_move_constructible<Hash>::value, ""); + static_assert(std::is_copy_assignable<Hash>::value, ""); + static_assert(std::is_move_assignable<Hash>::value, ""); + +#if TEST_STD_VER > 14 + static_assert(std::is_swappable<Hash>::value, ""); +#elif defined(_LIBCPP_VERSION) + static_assert(std::__is_swappable<Hash>::value, ""); +#endif + + // Hashable requirements + using CKey = ConvertibleTo<Key>; + static_assert(can_hash<Hash(Key&)>(), ""); + static_assert(can_hash<Hash(Key const&)>(), ""); + static_assert(can_hash<Hash(Key&&)>(), ""); + static_assert(can_hash<Hash const&(Key&)>(), ""); + static_assert(can_hash<Hash const&(Key const&)>(), ""); + static_assert(can_hash<Hash const&(Key&&)>(), ""); + + static_assert(can_hash<Hash(ConvertibleToSimple<Key>&)>(), ""); + static_assert(can_hash<Hash(ConvertibleToSimple<Key> const&)>(), ""); + static_assert(can_hash<Hash(ConvertibleToSimple<Key>&&)>(), ""); + + static_assert(can_hash<Hash(ConvertibleTo<Key>&)>(), ""); + static_assert(can_hash<Hash(ConvertibleTo<Key> const&)>(), ""); + static_assert(can_hash<Hash(ConvertibleTo<Key> &&)>(), ""); + static_assert(can_hash<Hash(ConvertibleTo<Key> const&&)>(), ""); + + const Hash h{}; + assert(h(key) == h(key)); + +} + +template <class Hash, class Key> +void test_hash_disabled() { + using namespace PoisonedHashDetail; + + // Disabled hash requirements + static_assert(!std::is_default_constructible<Hash>::value, ""); + static_assert(!std::is_copy_constructible<Hash>::value, ""); + static_assert(!std::is_move_constructible<Hash>::value, ""); + static_assert(!std::is_copy_assignable<Hash>::value, ""); + static_assert(!std::is_move_assignable<Hash>::value, ""); + + static_assert(!std::is_function< + typename std::remove_pointer< + typename std::remove_reference<Hash>::type + >::type + >::value, ""); + + // Hashable requirements + using CKey = ConvertibleTo<Key>; + static_assert(!can_hash<Hash(Key&)>(), ""); + static_assert(!can_hash<Hash(Key const&)>(), ""); + static_assert(!can_hash<Hash(Key&&)>(), ""); + static_assert(!can_hash<Hash const&(Key&)>(), ""); + static_assert(!can_hash<Hash const&(Key const&)>(), ""); + static_assert(!can_hash<Hash const&(Key&&)>(), ""); + + static_assert(!can_hash<Hash(ConvertibleToSimple<Key>&)>(), ""); + static_assert(!can_hash<Hash(ConvertibleToSimple<Key> const&)>(), ""); + static_assert(!can_hash<Hash(ConvertibleToSimple<Key>&&)>(), ""); + + static_assert(!can_hash<Hash(ConvertibleTo<Key>&)>(), ""); + static_assert(!can_hash<Hash(ConvertibleTo<Key> const&)>(), ""); + static_assert(!can_hash<Hash(ConvertibleTo<Key> &&)>(), ""); + static_assert(!can_hash<Hash(ConvertibleTo<Key> const&&)>(), ""); +} + + +template <class First, class ...Rest> +struct TypeList<First, Rest...> { + template <template <class> class Trait, bool Expect = true> + static constexpr bool assertTrait() { + static_assert(Trait<First>::value == Expect, ""); + return TypeList<Rest...>::template assertTrait<Trait, Expect>(); + } + + template <class Trait> + static void applyTrait() { + Trait::template apply<First>(); + TypeList<Rest...>::template applyTrait<Trait>(); + } +}; + +template <> +struct TypeList<> { + template <template <class> class Trait, bool Expect = true> + static constexpr bool assertTrait() { + return true; + } + template <class Trait> + static void applyTrait() {} +}; + + +struct TestLibraryTrait { + template <class Type> + static void apply() { test_hash_enabled<std::hash<Type>, Type>(); } +}; + +template <class Types> +void test_library_hash_specializations_available(Types) { + Types::template applyTrait<TestLibraryTrait >(); +} + +#endif // SUPPORT_POISONED_HASH_HELPER_HPP |