diff options
author | Eric Fiselier <eric@efcs.ca> | 2017-03-03 22:35:58 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2017-03-03 22:35:58 +0000 |
commit | bd6a2d850565341ff6fbf134998d48ad84d35585 (patch) | |
tree | 4fcbcf60038f02d81f5381fc166bfe1cb1fbf35f | |
parent | 7ee83b41e01e3377b6c96a418eea2bd8b8b5b940 (diff) | |
download | llvm-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_table | 7 | ||||
-rw-r--r-- | libcxx/include/utility | 11 | ||||
-rw-r--r-- | libcxx/test/libcxx/containers/unord/unord.set/missing_hash_specialization.fail.cpp | 38 |
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)); + } } |