Fix hash requirements check in __hash_table.

r296565 attempted to add better diagnostics when an unordered container
is instantiated with a hash that doesn't meet the Hash requirements.

However I mistakenly checked the wrong set of requirements. Specifically
it checked if the hash met the requirements for specializations of
std::hash. However these requirements are stricter than the generic
Hash requirements.

This patch fixes the assertions to only check the Hash requirements.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@296919 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2017-03-03 22:35:58 +00:00
parent c795c2a010
commit c1b1d7f0bb
3 changed files with 47 additions and 9 deletions

View File

@@ -871,16 +871,15 @@ public:
template <class _Key, class _Hash, class _Equal, class _Alloc> template <class _Key, class _Hash, class _Equal, class _Alloc>
struct __diagnose_hash_table_helper { struct __diagnose_hash_table_helper {
static constexpr bool __trigger_diagnostics() static constexpr bool __trigger_diagnostics()
_LIBCPP_DIAGNOSE_WARNING(__has_enabled_hash<_Key, _Hash>::value _LIBCPP_DIAGNOSE_WARNING(__check_hash_requirements<_Key, _Hash>::value
&& !__invokable<_Hash const&, _Key const&>::value, && !__invokable<_Hash const&, _Key const&>::value,
"the specified hash functor does not provide a const call operator") "the specified hash functor does not provide a const call operator")
_LIBCPP_DIAGNOSE_WARNING(is_copy_constructible<_Equal>::value _LIBCPP_DIAGNOSE_WARNING(is_copy_constructible<_Equal>::value
&& !__invokable<_Equal const&, _Key const&, _Key const&>::value, && !__invokable<_Equal const&, _Key const&, _Key const&>::value,
"the specified comparator type does not provide a const call operator") "the specified comparator type does not provide a const call operator")
{ {
static_assert(__has_enabled_hash<_Key, _Hash>::value, static_assert(__check_hash_requirements<_Key, _Hash>::value,
"the specified hash functor does not meet the requirements for an " "the specified hash does not meet the Hash requirements");
"enabled hash");
static_assert(is_copy_constructible<_Equal>::value, static_assert(is_copy_constructible<_Equal>::value,
"the specified comparator is required to be copy constructible"); "the specified comparator is required to be copy constructible");
return true; return true;

View File

@@ -1560,14 +1560,19 @@ struct _LIBCPP_TEMPLATE_VIS hash<nullptr_t>
#endif #endif
#ifndef _LIBCPP_CXX03_LANG #ifndef _LIBCPP_CXX03_LANG
template <class _Key, class _Hash = std::hash<_Key> > template <class _Key, class _Hash>
using __has_enabled_hash = integral_constant<bool, using __check_hash_requirements = integral_constant<bool,
is_default_constructible<_Hash>::value &&
is_copy_constructible<_Hash>::value && is_copy_constructible<_Hash>::value &&
is_move_constructible<_Hash>::value && is_move_constructible<_Hash>::value &&
__invokable_r<size_t, _Hash, _Key const&>::value __invokable_r<size_t, _Hash, _Key const&>::value
>; >;
template <class _Key, class _Hash = std::hash<_Key> >
using __has_enabled_hash = integral_constant<bool,
__check_hash_requirements<_Key, _Hash>::value &&
is_default_constructible<_Hash>::value
>;
#if _LIBCPP_STD_VER > 14 #if _LIBCPP_STD_VER > 14
template <class _Type, class> template <class _Type, class>
using __enable_hash_helper_imp = _Type; using __enable_hash_helper_imp = _Type;

View File

@@ -23,14 +23,48 @@
#include <utility> #include <utility>
using VT = std::pair<int, int>; using VT = std::pair<int, int>;
using Set = std::unordered_set<VT>;
struct BadHashNoCopy {
BadHashNoCopy() = default;
BadHashNoCopy(BadHashNoCopy const&) = delete;
template <class T>
size_t operator()(T const&) const { return 0; }
};
struct BadHashNoCall {
};
struct GoodHashNoDefault {
explicit GoodHashNoDefault(void*) {}
template <class T>
size_t operator()(T const&) const { return 0; }
};
int main() { int main() {
Set s; // expected-error@__hash_table:* {{the specified hash functor does not meet the requirements for an enabled hash}} {
using Set = std::unordered_set<VT>;
Set s; // expected-error@__hash_table:* {{the specified hash does not meet the Hash requirements}}
// FIXME: It would be great to suppress the below diagnostic all together. // FIXME: It would be great to suppress the below diagnostic all together.
// but for now it's sufficient that it appears last. However there is // but for now it's sufficient that it appears last. However there is
// currently no way to test the order diagnostics are issued. // currently no way to test the order diagnostics are issued.
// expected-error@memory:* {{call to implicitly-deleted default constructor of 'std::__1::hash<std::__1::pair<int, int> >'}} // expected-error@memory:* {{call to implicitly-deleted default constructor of 'std::__1::hash<std::__1::pair<int, int> >'}}
}
{
using Set = std::unordered_set<int, BadHashNoCopy>;
Set s; // expected-error@__hash_table:* {{the specified hash does not meet the Hash requirements}}
}
{
using Set = std::unordered_set<int, BadHashNoCall>;
Set s; // expected-error@__hash_table:* {{the specified hash does not meet the Hash requirements}}
}
{
using Set = std::unordered_set<int, GoodHashNoDefault>;
Set s(/*bucketcount*/42, GoodHashNoDefault(nullptr));
}
} }