diff options
author | Eric Fiselier <eric@efcs.ca> | 2017-01-21 00:02:12 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2017-01-21 00:02:12 +0000 |
commit | f9127593a9fd0ef585fed52f838d89c825b8cd72 (patch) | |
tree | 62a21ebb1691065d901bb9bb1294b3a2bd56021f /libcxx/test | |
parent | 59e1df524f41d3c2ba1de62f4abea1582cc7332e (diff) | |
download | bcm5719-llvm-f9127593a9fd0ef585fed52f838d89c825b8cd72.tar.gz bcm5719-llvm-f9127593a9fd0ef585fed52f838d89c825b8cd72.zip |
Implement P0513R0 - "Poisoning the Hash"
Summary:
Exactly what the title says.
This patch also adds a `std::hash<nullptr_t>` specialization in C++17, but it was not added by this paper and I can't find the actual paper that adds it.
See http://wg21.link/P0513R0 for more info.
If there are no comments in the next couple of days I'll commit this
Reviewers: mclow.lists, K-ballo, EricWF
Reviewed By: EricWF
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D28938
llvm-svn: 292684
Diffstat (limited to 'libcxx/test')
23 files changed, 812 insertions, 47 deletions
diff --git a/libcxx/test/libcxx/test/config.py b/libcxx/test/libcxx/test/config.py index ae48c735a23..0d90258267a 100644 --- a/libcxx/test/libcxx/test/config.py +++ b/libcxx/test/libcxx/test/config.py @@ -423,15 +423,6 @@ class Configuration(object): if not std: # Choose the newest possible language dialect if none is given. possible_stds = ['c++1z', 'c++14', 'c++11', 'c++03'] - if self.cxx.type == 'gcc': - maj_v, _, _ = self.cxx.version - maj_v = int(maj_v) - if maj_v < 7: - possible_stds.remove('c++1z') - # FIXME: How many C++14 tests actually fail under GCC 5 and 6? - # Should we XFAIL them individually instead? - if maj_v <= 6: - possible_stds.remove('c++14') for s in possible_stds: if self.cxx.hasCompileFlag('-std=%s' % s): std = s diff --git a/libcxx/test/std/containers/sequences/vector.bool/enabled_hash.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/enabled_hash.pass.cpp new file mode 100644 index 00000000000..f8a8dbd8d6d --- /dev/null +++ b/libcxx/test/std/containers/sequences/vector.bool/enabled_hash.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <vector> + +// Test that <vector> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <vector> + +#include "poisoned_hash_helper.hpp" +#include "min_allocator.h" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::vector<bool> >(); + test_hash_enabled_for_type<std::vector<bool, min_allocator<bool>>>(); + } +} diff --git a/libcxx/test/std/diagnostics/syserr/syserr.hash/enabled_hash.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..f1b46057591 --- /dev/null +++ b/libcxx/test/std/diagnostics/syserr/syserr.hash/enabled_hash.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <system_error> + +// Test that <system_error> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <system_error> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::error_code>(); + test_hash_enabled_for_type<std::error_condition>(); + } +} diff --git a/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp b/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp new file mode 100644 index 00000000000..01f01218999 --- /dev/null +++ b/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <string> + +// Test that <string> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <string> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::string>(); + test_hash_enabled_for_type<std::wstring>(); +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test_hash_enabled_for_type<std::u16string>(); + test_hash_enabled_for_type<std::u32string>(); +#endif + } +} diff --git a/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp b/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp new file mode 100644 index 00000000000..2e9ebcb4c03 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <string_view> + +// Test that <string_view> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <string_view> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::string_view>(); + test_hash_enabled_for_type<std::wstring_view>(); +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test_hash_enabled_for_type<std::u16string_view>(); + test_hash_enabled_for_type<std::u32string_view>(); +#endif + } +} diff --git a/libcxx/test/std/strings/string.view/string.view.hash/string_view.pass.cpp b/libcxx/test/std/strings/string.view/string.view.hash/string_view.pass.cpp index 63099e2c886..d0766482551 100644 --- a/libcxx/test/std/strings/string.view/string.view.hash/string_view.pass.cpp +++ b/libcxx/test/std/strings/string.view/string.view.hash/string_view.pass.cpp @@ -19,29 +19,38 @@ // Not very portable #include <string_view> +#include <string> #include <cassert> #include <type_traits> using std::string_view; -template <class T> +template <class SV> void test() { - typedef std::hash<T> H; - static_assert((std::is_same<typename H::argument_type, T>::value), "" ); + typedef std::hash<SV> H; + static_assert((std::is_same<typename H::argument_type, SV>::value), "" ); static_assert((std::is_same<typename H::result_type, std::size_t>::value), "" ); - H h; -// std::string g1 = "1234567890"; -// std::string g2 = "1234567891"; - typedef typename T::value_type char_type; + + typedef typename SV::value_type char_type; + typedef std::basic_string<char_type> String; + typedef std::hash<String> SH; + char_type g1 [ 10 ]; char_type g2 [ 10 ]; for ( int i = 0; i < 10; ++i ) g1[i] = g2[9-i] = static_cast<char_type>('0' + i); - T s1(g1, 10); - T s2(g2, 10); + H h; + SH sh; + SV s1(g1, 10); + String ss1(s1); + SV s2(g2, 10); + String ss2(s2); + assert(h(s1) == h(s1)); assert(h(s1) != h(s2)); + assert(sh(ss1) == h(s1)); + assert(sh(ss2) == h(s2)); } int main() diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/enabled_hashes.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/enabled_hashes.pass.cpp new file mode 100644 index 00000000000..9799467c452 --- /dev/null +++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/enabled_hashes.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-threads +// UNSUPPORTED: c++98, c++03 + +// <thread> + +// Test that <thread> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <thread> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::thread::id>(); + } +} diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/thread_id.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/thread_id.pass.cpp index 106c69e2e4a..f7d4deb8f98 100644 --- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/thread_id.pass.cpp +++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/thread_id.pass.cpp @@ -9,7 +9,7 @@ // // UNSUPPORTED: libcpp-has-no-threads -// <functional> +// <thread> // template <class T> // struct hash diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp new file mode 100644 index 00000000000..775247fc80b --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <functional> + +// Test that <functional> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <functional> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); +} diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/enabled_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..e9237c534fb --- /dev/null +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/enabled_hash.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <memory> + +// Test that <memory> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <memory> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); +} diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_shared_ptr.pass.cpp index 990cb58722b..5fba1fc0460 100644 --- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_shared_ptr.pass.cpp +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_shared_ptr.pass.cpp @@ -20,11 +20,25 @@ #include <memory> #include <cassert> +#if TEST_STD_VER >= 11 +#include "poisoned_hash_helper.hpp" + +struct A {}; +#endif + int main() { + { int* ptr = new int; std::shared_ptr<int> p(ptr); std::hash<std::shared_ptr<int> > f; std::size_t h = f(p); assert(h == std::hash<int*>()(ptr)); + } +#if TEST_STD_VER >= 11 + { + test_hash_enabled_for_type<std::shared_ptr<int>>(); + test_hash_enabled_for_type<std::shared_ptr<A>>(); + } +#endif } diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_unique_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_unique_ptr.pass.cpp index 5cd4ab1f83d..f989a017348 100644 --- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_unique_ptr.pass.cpp +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.hash/hash_unique_ptr.pass.cpp @@ -20,11 +20,76 @@ #include <memory> #include <cassert> +#include "test_macros.h" + +#if TEST_STD_VER >= 11 +#include "poisoned_hash_helper.hpp" +#include "deleter_types.h" +#include "min_allocator.h" + +template <class ValueT, class Del> +void test_enabled_with_deleter() { + using UPtr = std::unique_ptr<ValueT, Del>; + using pointer = typename UPtr::pointer; + using RawDel = typename std::decay<Del>::type; + RawDel d(1); + UPtr p(nullptr, std::forward<Del>(d)); + test_hash_enabled_for_type<UPtr>(p); + test_hash_enabled_for_type<pointer>(); +} + +template <class ValueT, class Del> +void test_disabled_with_deleter() { + using UPtr = std::unique_ptr<ValueT, Del>; + using pointer = typename UPtr::pointer; + test_hash_disabled_for_type<UPtr>(); + test_hash_disabled_for_type<pointer>(); +} + +template <class T> +struct std::hash<min_pointer<T, std::integral_constant<size_t, 1>>> { + size_t operator()(min_pointer<T, std::integral_constant<size_t, 1>> p) const { + if (!p) return 0; + return std::hash<T*>{}(std::addressof(*p)); + } +}; + +struct A {}; + +#endif // TEST_STD_VER >= 11 + int main() { + { int* ptr = new int; std::unique_ptr<int> p(ptr); std::hash<std::unique_ptr<int> > f; std::size_t h = f(p); assert(h == std::hash<int*>()(ptr)); + } +#if TEST_STD_VER >= 11 + { + test_enabled_with_deleter<int, Deleter<int>>(); + test_enabled_with_deleter<int[], Deleter<int[]>>(); + test_enabled_with_deleter<int, CopyDeleter<int>>(); + test_enabled_with_deleter<int, CopyDeleter<int[]>>(); + test_enabled_with_deleter<int, NCDeleter<int>&>(); + test_enabled_with_deleter<int[], NCDeleter<int[]>&>(); + test_enabled_with_deleter<int, NCConstDeleter<int> const&>(); + test_enabled_with_deleter<int[], NCConstDeleter<int[]> const&>(); + } + { + test_enabled_with_deleter<int, PointerDeleter<int, 1>>(); + test_enabled_with_deleter<int[], PointerDeleter<int[], 1>>(); + test_enabled_with_deleter<A, PointerDeleter<A, 1>>(); + test_enabled_with_deleter<A[], PointerDeleter<A[], 1>>(); + +#if TEST_STD_VER > 14 + test_disabled_with_deleter<int, PointerDeleter<int, 0>>(); + test_disabled_with_deleter<int[], PointerDeleter<int[], 0>>(); + test_disabled_with_deleter<A, PointerDeleter<A, 0>>(); + test_disabled_with_deleter<A[], PointerDeleter<A[], 0>>(); +#endif + } +#endif } diff --git a/libcxx/test/std/utilities/optional/optional.hash/enabled_hash.pass.cpp b/libcxx/test/std/utilities/optional/optional.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..e54a4ced841 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.hash/enabled_hash.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++11, c++14 + +// <optional> + +// Test that <optional> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <optional> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + + } +} diff --git a/libcxx/test/std/utilities/optional/optional.hash/hash.pass.cpp b/libcxx/test/std/utilities/optional/optional.hash/hash.pass.cpp index dfdd07ddf45..ea89dc3bd92 100644 --- a/libcxx/test/std/utilities/optional/optional.hash/hash.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.hash/hash.pass.cpp @@ -17,6 +17,15 @@ #include <memory> #include <cassert> +#include "poisoned_hash_helper.hpp" + +struct A {}; +struct B {}; + +template <> +struct std::hash<B> { + size_t operator()(B const&) { return 0; } +}; int main() { @@ -45,4 +54,16 @@ int main() opt = std::unique_ptr<int>(new int(3)); assert(std::hash<optional<T>>{}(opt) == std::hash<T>{}(*opt)); } + { + test_hash_enabled_for_type<std::optional<int> >(); + test_hash_enabled_for_type<std::optional<int*> >(); + test_hash_enabled_for_type<std::optional<const int> >(); + test_hash_enabled_for_type<std::optional<int* const> >(); + + test_hash_disabled_for_type<std::optional<A>>(); + test_hash_disabled_for_type<std::optional<const A>>(); + + test_hash_enabled_for_type<std::optional<B>>(); + test_hash_enabled_for_type<std::optional<const B>>(); + } } diff --git a/libcxx/test/std/utilities/template.bitset/bitset.hash/enabled_hash.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..1d8bff41ae5 --- /dev/null +++ b/libcxx/test/std/utilities/template.bitset/bitset.hash/enabled_hash.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <bitset> + +// Test that <bitset> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <bitset> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); + { + test_hash_enabled_for_type<std::bitset<0> >(); + test_hash_enabled_for_type<std::bitset<1> >(); + test_hash_enabled_for_type<std::bitset<1024> >(); + test_hash_enabled_for_type<std::bitset<100000> >(); + } +} diff --git a/libcxx/test/std/utilities/type.index/type.index.hash/enabled_hash.pass.cpp b/libcxx/test/std/utilities/type.index/type.index.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..26bada38fdd --- /dev/null +++ b/libcxx/test/std/utilities/type.index/type.index.hash/enabled_hash.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <typeindex> + +// Test that <typeindex> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <typeindex> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); +} diff --git a/libcxx/test/std/utilities/type.index/type.index.synopsis/hash_type_index.pass.cpp b/libcxx/test/std/utilities/type.index/type.index.synopsis/hash_type_index.pass.cpp index 6d353f1d305..0fe020bf3d4 100644 --- a/libcxx/test/std/utilities/type.index/type.index.synopsis/hash_type_index.pass.cpp +++ b/libcxx/test/std/utilities/type.index/type.index.synopsis/hash_type_index.pass.cpp @@ -18,9 +18,19 @@ #include <typeindex> #include <type_traits> +#include "test_macros.h" +#if TEST_STD_VER >= 11 +#include "poisoned_hash_helper.hpp" +#endif + int main() { + { typedef std::hash<std::type_index> H; static_assert((std::is_same<typename H::argument_type, std::type_index>::value), "" ); static_assert((std::is_same<typename H::result_type, std::size_t>::value), "" ); + } + { + test_hash_enabled_for_type<std::type_index>(std::type_index(typeid(int))); + } } diff --git a/libcxx/test/std/utilities/variant/variant.hash/enabled_hash.pass.cpp b/libcxx/test/std/utilities/variant/variant.hash/enabled_hash.pass.cpp new file mode 100644 index 00000000000..826dcbae6b9 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.hash/enabled_hash.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++11, c++14 + +// <variant> + +// Test that <variant> provides all of the arithmetic, enum, and pointer +// hash specializations. + +#include <variant> + +#include "poisoned_hash_helper.hpp" + +int main() { + test_library_hash_specializations_available(); +} diff --git a/libcxx/test/std/utilities/variant/variant.hash/hash.pass.cpp b/libcxx/test/std/utilities/variant/variant.hash/hash.pass.cpp index d807a7c7e2e..96ef967f6c1 100644 --- a/libcxx/test/std/utilities/variant/variant.hash/hash.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.hash/hash.pass.cpp @@ -21,6 +21,7 @@ #include "test_macros.h" #include "variant_test_helpers.hpp" +#include "poisoned_hash_helper.hpp" #ifndef TEST_HAS_NO_EXCEPTIONS namespace std { @@ -103,6 +104,9 @@ void test_hash_monostate() { ASSERT_SAME_TYPE(decltype(h(m1)), std::size_t); static_assert(std::is_copy_constructible<H>::value, ""); } + { + test_hash_enabled_for_type<std::monostate>(); + } } void test_hash_variant_duplicate_elements() { @@ -117,8 +121,34 @@ void test_hash_variant_duplicate_elements() { LIBCPP_ASSERT(h(v1) != h(v2)); } +struct A {}; +struct B {}; + +template <> +struct std::hash<B> { + size_t operator()(B const&) const { + return 0; + } +}; + +void test_hash_variant_enabled() { + { + test_hash_enabled_for_type<std::variant<int> >(); + test_hash_enabled_for_type<std::variant<int*, long, double, const int> >(); + } + { + test_hash_disabled_for_type<std::variant<int, A>>(); + test_hash_disabled_for_type<std::variant<const A, void*>>(); + } + { + test_hash_enabled_for_type<std::variant<int, B>>(); + test_hash_enabled_for_type<std::variant<const B, int>>(); + } +} + int main() { test_hash_variant(); test_hash_variant_duplicate_elements(); test_hash_monostate(); + test_hash_variant_enabled(); } 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>(), ""); +} |