aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2017-03-03 22:35:58 +0000
committerEric Fiselier <eric@efcs.ca>2017-03-03 22:35:58 +0000
commitbd6a2d850565341ff6fbf134998d48ad84d35585 (patch)
tree4fcbcf60038f02d81f5381fc166bfe1cb1fbf35f
parent7ee83b41e01e3377b6c96a418eea2bd8b8b5b940 (diff)
downloadllvm-bd6a2d850565341ff6fbf134998d48ad84d35585.zip
llvm-bd6a2d850565341ff6fbf134998d48ad84d35585.tar.gz
llvm-bd6a2d850565341ff6fbf134998d48ad84d35585.tar.bz2
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. llvm-svn: 296919
-rw-r--r--libcxx/include/__hash_table7
-rw-r--r--libcxx/include/utility11
-rw-r--r--libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp38
3 files changed, 47 insertions, 9 deletions
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 9d9a290..2161493 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -871,16 +871,15 @@ public:
template <class _Key, class _Hash, class _Equal, class _Alloc>
struct __diagnose_hash_table_helper {
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,
"the specified hash functor does not provide a const call operator")
_LIBCPP_DIAGNOSE_WARNING(is_copy_constructible<_Equal>::value
&& !__invokable<_Equal const&, _Key const&, _Key const&>::value,
"the specified comparator type does not provide a const call operator")
{
- static_assert(__has_enabled_hash<_Key, _Hash>::value,
- "the specified hash functor does not meet the requirements for an "
- "enabled hash");
+ static_assert(__check_hash_requirements<_Key, _Hash>::value,
+ "the specified hash does not meet the Hash requirements");
static_assert(is_copy_constructible<_Equal>::value,
"the specified comparator is required to be copy constructible");
return true;
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 3452fe1..1f41c07 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -1560,14 +1560,19 @@ struct _LIBCPP_TEMPLATE_VIS hash<nullptr_t>
#endif
#ifndef _LIBCPP_CXX03_LANG
-template <class _Key, class _Hash = std::hash<_Key> >
-using __has_enabled_hash = integral_constant<bool,
- is_default_constructible<_Hash>::value &&
+template <class _Key, class _Hash>
+using __check_hash_requirements = integral_constant<bool,
is_copy_constructible<_Hash>::value &&
is_move_constructible<_Hash>::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
template <class _Type, class>
using __enable_hash_helper_imp = _Type;
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp b/libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp
index e182a03..d682e74 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp
@@ -23,14 +23,48 @@
#include <utility>
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() {
- 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.
// but for now it's sufficient that it appears last. However there is
// 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> >'}}
+ }
+ {
+ 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));
+ }
}