diff options
Diffstat (limited to 'libcxx/test/support')
-rw-r--r-- | libcxx/test/support/deleter_types.h | 60 | ||||
-rw-r--r-- | libcxx/test/support/min_allocator.h | 56 | ||||
-rw-r--r-- | libcxx/test/support/poisoned_hash_helper.hpp | 244 | ||||
-rw-r--r-- | libcxx/test/support/test.support/test_poisoned_hash_helper.pass.cpp | 30 |
4 files changed, 362 insertions, 28 deletions
diff --git a/libcxx/test/support/deleter_types.h b/libcxx/test/support/deleter_types.h index c8b38202352..f7cdb624e4f 100644 --- a/libcxx/test/support/deleter_types.h +++ b/libcxx/test/support/deleter_types.h @@ -21,6 +21,7 @@ #include <cassert> #include "test_macros.h" +#include "min_allocator.h" #if TEST_STD_VER >= 11 @@ -379,4 +380,63 @@ swap(test_deleter<T>& x, test_deleter<T>& y) y = std::move(t); } +#if TEST_STD_VER >= 11 + +template <class T, size_t ID = 0> +class PointerDeleter +{ + PointerDeleter(const PointerDeleter&); + PointerDeleter& operator=(const PointerDeleter&); + +public: + typedef min_pointer<T, std::integral_constant<size_t, ID>> pointer; + + PointerDeleter() = default; + PointerDeleter(PointerDeleter&&) = default; + PointerDeleter& operator=(PointerDeleter&&) = default; + explicit PointerDeleter(int) {} + + template <class U> + PointerDeleter(PointerDeleter<U, ID>&&, + typename std::enable_if<!std::is_same<U, T>::value>::type* = 0) + {} + + void operator()(pointer p) { if (p) { delete std::addressof(*p); }} + +private: + template <class U> + PointerDeleter(const PointerDeleter<U, ID>&, + typename std::enable_if<!std::is_same<U, T>::value>::type* = 0); +}; + + +template <class T, size_t ID> +class PointerDeleter<T[], ID> +{ + PointerDeleter(const PointerDeleter&); + PointerDeleter& operator=(const PointerDeleter&); + +public: + typedef min_pointer<T, std::integral_constant<size_t, ID> > pointer; + + PointerDeleter() = default; + PointerDeleter(PointerDeleter&&) = default; + PointerDeleter& operator=(PointerDeleter&&) = default; + explicit PointerDeleter(int) {} + + template <class U> + PointerDeleter(PointerDeleter<U, ID>&&, + typename std::enable_if<!std::is_same<U, T>::value>::type* = 0) + {} + + void operator()(pointer p) { if (p) { delete [] std::addressof(*p); }} + +private: + template <class U> + PointerDeleter(const PointerDeleter<U, ID>&, + typename std::enable_if<!std::is_same<U, T>::value>::type* = 0); +}; + +#endif // TEST_STD_VER >= 11 + #endif // SUPPORT_DELETER_TYPES_H diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h index d518e4a6d77..a3af9e1db66 100644 --- a/libcxx/test/support/min_allocator.h +++ b/libcxx/test/support/min_allocator.h @@ -136,31 +136,31 @@ public: #include <memory> -template <class T> class min_pointer; -template <class T> class min_pointer<const T>; -template <> class min_pointer<void>; -template <> class min_pointer<const void>; +template <class T, class = std::integral_constant<size_t, 0> > class min_pointer; +template <class T, class ID> class min_pointer<const T, ID>; +template <class ID> class min_pointer<void, ID>; +template <class ID> class min_pointer<const void, ID>; template <class T> class min_allocator; -template <> -class min_pointer<const void> +template <class ID> +class min_pointer<const void, ID> { const void* ptr_; public: min_pointer() TEST_NOEXCEPT = default; min_pointer(std::nullptr_t) TEST_NOEXCEPT : ptr_(nullptr) {} template <class T> - min_pointer(min_pointer<T> p) TEST_NOEXCEPT : ptr_(p.ptr_) {} + min_pointer(min_pointer<T, ID> p) TEST_NOEXCEPT : ptr_(p.ptr_) {} explicit operator bool() const {return ptr_ != nullptr;} friend bool operator==(min_pointer x, min_pointer y) {return x.ptr_ == y.ptr_;} friend bool operator!=(min_pointer x, min_pointer y) {return !(x == y);} - template <class U> friend class min_pointer; + template <class U, class XID> friend class min_pointer; }; -template <> -class min_pointer<void> +template <class ID> +class min_pointer<void, ID> { void* ptr_; public: @@ -172,16 +172,16 @@ public: !std::is_const<T>::value >::type > - min_pointer(min_pointer<T> p) TEST_NOEXCEPT : ptr_(p.ptr_) {} + min_pointer(min_pointer<T, ID> p) TEST_NOEXCEPT : ptr_(p.ptr_) {} explicit operator bool() const {return ptr_ != nullptr;} friend bool operator==(min_pointer x, min_pointer y) {return x.ptr_ == y.ptr_;} friend bool operator!=(min_pointer x, min_pointer y) {return !(x == y);} - template <class U> friend class min_pointer; + template <class U, class XID> friend class min_pointer; }; -template <class T> +template <class T, class ID> class min_pointer { T* ptr_; @@ -190,7 +190,7 @@ class min_pointer public: min_pointer() TEST_NOEXCEPT = default; min_pointer(std::nullptr_t) TEST_NOEXCEPT : ptr_(nullptr) {} - explicit min_pointer(min_pointer<void> p) TEST_NOEXCEPT : ptr_(static_cast<T*>(p.ptr_)) {} + explicit min_pointer(min_pointer<void, ID> p) TEST_NOEXCEPT : ptr_(static_cast<T*>(p.ptr_)) {} explicit operator bool() const {return ptr_ != nullptr;} @@ -247,12 +247,12 @@ public: friend bool operator==(min_pointer x, min_pointer y) {return x.ptr_ == y.ptr_;} friend bool operator!=(min_pointer x, min_pointer y) {return !(x == y);} - template <class U> friend class min_pointer; + template <class U, class XID> friend class min_pointer; template <class U> friend class min_allocator; }; -template <class T> -class min_pointer<const T> +template <class T, class ID> +class min_pointer<const T, ID> { const T* ptr_; @@ -260,8 +260,8 @@ class min_pointer<const T> public: min_pointer() TEST_NOEXCEPT = default; min_pointer(std::nullptr_t) : ptr_(nullptr) {} - min_pointer(min_pointer<T> p) : ptr_(p.ptr_) {} - explicit min_pointer(min_pointer<const void> p) : ptr_(static_cast<const T*>(p.ptr_)) {} + min_pointer(min_pointer<T, ID> p) : ptr_(p.ptr_) {} + explicit min_pointer(min_pointer<const void, ID> p) : ptr_(static_cast<const T*>(p.ptr_)) {} explicit operator bool() const {return ptr_ != nullptr;} @@ -318,37 +318,37 @@ public: friend bool operator==(min_pointer x, min_pointer y) {return x.ptr_ == y.ptr_;} friend bool operator!=(min_pointer x, min_pointer y) {return !(x == y);} - template <class U> friend class min_pointer; + template <class U, class XID> friend class min_pointer; }; -template <class T> +template <class T, class ID> inline bool -operator==(min_pointer<T> x, std::nullptr_t) +operator==(min_pointer<T, ID> x, std::nullptr_t) { return !static_cast<bool>(x); } -template <class T> +template <class T, class ID> inline bool -operator==(std::nullptr_t, min_pointer<T> x) +operator==(std::nullptr_t, min_pointer<T, ID> x) { return !static_cast<bool>(x); } -template <class T> +template <class T, class ID> inline bool -operator!=(min_pointer<T> x, std::nullptr_t) +operator!=(min_pointer<T, ID> x, std::nullptr_t) { return static_cast<bool>(x); } -template <class T> +template <class T, class ID> inline bool -operator!=(std::nullptr_t, min_pointer<T> x) +operator!=(std::nullptr_t, min_pointer<T, ID> x) { return static_cast<bool>(x); } diff --git a/libcxx/test/support/poisoned_hash_helper.hpp b/libcxx/test/support/poisoned_hash_helper.hpp new file mode 100644 index 00000000000..0a5675a2c5a --- /dev/null +++ b/libcxx/test/support/poisoned_hash_helper.hpp @@ -0,0 +1,244 @@ +// -*- 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" + +#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 TEST_STD_VER > 14 + decltype(nullptr), +#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 diff --git a/libcxx/test/support/test.support/test_poisoned_hash_helper.pass.cpp b/libcxx/test/support/test.support/test_poisoned_hash_helper.pass.cpp new file mode 100644 index 00000000000..4dffae6dbdf --- /dev/null +++ b/libcxx/test/support/test.support/test_poisoned_hash_helper.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// Test that the header `poisoned_hash_helper.hpp` doesn't include any +// headers that provide hash<T> specializations. This is required so that the +// 'test_library_hash_specializations_available()' function returns false +// by default, unless a STL header providing hash has already been included. + +#include "poisoned_hash_helper.hpp" + +template <class T, size_t = sizeof(T)> +constexpr bool is_complete_imp(int) { return true; } +template <class> constexpr bool is_complete_imp(long) { return false; } +template <class T> constexpr bool is_complete() { return is_complete_imp<T>(0); } + +template <class T> struct has_complete_hash { + enum { value = is_complete<std::hash<T> >() }; +}; + +int main() { + static_assert(LibraryHashTypes::assertTrait<has_complete_hash, false>(), ""); +} |