// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef SUPPORT_POISONED_HASH_HELPER_H
#define SUPPORT_POISONED_HASH_HELPER_H
#include <functional>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>
#include "test_macros.h"
#include "type_algorithms.h"
template <class Hash, class Key, class Res = decltype(std::declval<Hash&>()(std::declval<Key>()))>
constexpr bool can_hash_impl(int) {
return std::is_same<Res, std::size_t>::value;
}
template <class, class>
constexpr bool can_hash_impl(long) {
return false;
}
template <class Hash, class Key>
constexpr bool can_hash() {
return can_hash_impl<Hash, Key>(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); }
};
// Test that the specified Hash meets the requirements of an enabled hash
template <class Key, class Hash = std::hash<Key>>
TEST_CONSTEXPR_CXX20 void test_hash_enabled(Key const& key = Key{}) {
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_v<Hash>, "");
#endif
// Hashable requirements
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));
}
// Test that the specified Hash meets the requirements of a disabled hash.
template <class Key, class Hash = std::hash<Key>>
void test_hash_disabled() {
// 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
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&&>(), "");
}
enum Enum {};
enum EnumClass : bool {};
struct Class {};
// Each header that declares the std::hash template provides enabled
// specializations of std::hash for std::nullptr_t and all cv-unqualified
// arithmetic, enumeration, and pointer types.
#if TEST_STD_VER >= 17
using MaybeNullptr = types::type_list<std::nullptr_t>;
#else
using MaybeNullptr = types::type_list<>;
#endif
using LibraryHashTypes = types::
concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
struct TestHashEnabled {
template <class T>
void operator()() const {
test_hash_enabled<T>();
}
};
// 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::for_each(Types(), TestHashEnabled());
}
#endif // SUPPORT_POISONED_HASH_HELPER_H