Index: libcxx/docs/UsingLibcxx.rst =================================================================== --- libcxx/docs/UsingLibcxx.rst +++ libcxx/docs/UsingLibcxx.rst @@ -182,8 +182,10 @@ This macro disables the additional diagnostics generated by libc++ using the `diagnose_if` attribute. These additional diagnostics include checks for: - * Giving `set`, `map`, `multiset`, `multimap` a comparator which is not - const callable. + * Giving `set`, `map`, `multiset`, `multimap` and their `unordered_` + counterparts a comparator which is not const callable. + * Giving an unordered associative container a hasher that is not const + callable. **_LIBCPP_NO_VCRUNTIME**: Microsoft's C and C++ headers are fairly entangled, and some of their C++ Index: libcxx/include/__hash_table =================================================================== --- libcxx/include/__hash_table +++ libcxx/include/__hash_table @@ -35,15 +35,6 @@ template struct __hash_value_type; -template ::value && !__libcpp_is_final<_Hash>::value> -class __unordered_map_hasher; - -template ::value && !__libcpp_is_final<_Pred>::value - > -class __unordered_map_equal; - #ifndef _LIBCPP_CXX03_LANG template struct __is_hash_value_type_imp : false_type {}; @@ -859,36 +850,32 @@ template friend class __hash_map_node_destructor; }; - +template +struct __enforce_unordered_container_requirements { #ifndef _LIBCPP_CXX03_LANG -template -struct __diagnose_hash_table_helper { - static constexpr bool __trigger_diagnostics() - _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(__check_hash_requirements<_Key, _Hash>::value, - "the specified hash does not meet the Hash requirements"); + "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; - } + "the specified comparator is required to be copy constructible"); +#endif + typedef int type; }; -template -struct __diagnose_hash_table_helper< - __hash_value_type<_Key, _Value>, - __unordered_map_hasher<_Key, __hash_value_type<_Key, _Value>, _Hash>, - __unordered_map_equal<_Key, __hash_value_type<_Key, _Value>, _Equal>, - _Alloc> -: __diagnose_hash_table_helper<_Key, _Hash, _Equal, _Alloc> -{ -}; -#endif // _LIBCPP_CXX03_LANG +template +#ifndef _LIBCPP_CXX03_LANG + _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Equal const&, _Key const&, _Key const&>::value, + "the specified comparator type does not provide a const call operator") + _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Hash const&, _Key const&>::value, + "the specified hash functor does not provide a const call operator") +#endif +typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type +__diagnose_unordered_container_requirements(int); + +// This dummy overload is used so that the compiler won't emit a spurious +// "no matching function for call to __diagnose_unordered_xxx" diagnostic +// when the overload above causes a hard error. +template +int __diagnose_unordered_container_requirements(void*); template class __hash_table @@ -952,10 +939,6 @@ typedef allocator_traits<__pointer_allocator> __pointer_alloc_traits; typedef typename __bucket_list_deleter::pointer __node_pointer_pointer; -#ifndef _LIBCPP_CXX03_LANG - static_assert(__diagnose_hash_table_helper<_Tp, _Hash, _Equal, _Alloc>::__trigger_diagnostics(), ""); -#endif - // --- Member data begin --- __bucket_list __bucket_list_; __compressed_pair<__first_node, __node_allocator> __p1_; Index: libcxx/include/__tree =================================================================== --- libcxx/include/__tree +++ libcxx/include/__tree @@ -40,10 +40,6 @@ template struct __value_type; -template ::value && !__libcpp_is_final<_Compare>::value> -class __map_value_compare; - template class __map_node_destructor; template class _LIBCPP_TEMPLATE_VIS __map_iterator; template class _LIBCPP_TEMPLATE_VIS __map_const_iterator; @@ -956,24 +952,12 @@ }; +template #ifndef _LIBCPP_CXX03_LANG -template -struct __diagnose_tree_helper { - static constexpr bool __trigger_diagnostics() - _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Compare const&, _Tp const&, _Tp const&>::value, - "the specified comparator type does not provide a const call operator") - { return true; } -}; - -template -struct __diagnose_tree_helper< - __value_type<_Key, _Value>, - __map_value_compare<_Key, __value_type<_Key, _Value>, _KeyComp>, - _Alloc -> : __diagnose_tree_helper<_Key, _KeyComp, _Alloc> -{ -}; -#endif // !_LIBCPP_CXX03_LANG + _LIBCPP_DIAGNOSE_WARNING(!std::__invokable<_Compare const&, _Tp const&, _Tp const&>::value, + "the specified comparator type does not provide a const call operator") +#endif +int __diagnose_non_const_comparator(); template class __tree @@ -1807,10 +1791,6 @@ { static_assert((is_copy_constructible::value), "Comparator must be copy-constructible."); -#ifndef _LIBCPP_CXX03_LANG - static_assert((__diagnose_tree_helper<_Tp, _Compare, _Allocator>:: - __trigger_diagnostics()), ""); -#endif destroy(__root()); } Index: libcxx/include/map =================================================================== --- libcxx/include/map +++ libcxx/include/map @@ -453,7 +453,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template +template ::value && !__libcpp_is_final<_Compare>::value> class __map_value_compare : private _Compare { @@ -867,6 +868,7 @@ typedef value_type& reference; typedef const value_type& const_reference; + static_assert(sizeof(__diagnose_non_const_comparator<_Key, _Compare>()), ""); static_assert((is_same::value), "Allocator::value_type must be same type as value_type"); @@ -1522,6 +1524,7 @@ typedef value_type& reference; typedef const value_type& const_reference; + static_assert(sizeof(__diagnose_non_const_comparator<_Key, _Compare>()), ""); static_assert((is_same::value), "Allocator::value_type must be same type as value_type"); Index: libcxx/include/set =================================================================== --- libcxx/include/set +++ libcxx/include/set @@ -409,6 +409,7 @@ typedef value_type& reference; typedef const value_type& const_reference; + static_assert(sizeof(__diagnose_non_const_comparator<_Key, _Compare>()), ""); static_assert((is_same::value), "Allocator::value_type must be same type as value_type"); @@ -818,6 +819,7 @@ typedef value_type& reference; typedef const value_type& const_reference; + static_assert(sizeof(__diagnose_non_const_comparator<_Key, _Compare>()), ""); static_assert((is_same::value), "Allocator::value_type must be same type as value_type"); Index: libcxx/include/unordered_map =================================================================== --- libcxx/include/unordered_map +++ libcxx/include/unordered_map @@ -379,7 +379,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template +template ::value && !__libcpp_is_final<_Hash>::value> class __unordered_map_hasher : private _Hash { @@ -447,7 +448,8 @@ __x.swap(__y); } -template +template ::value && !__libcpp_is_final<_Pred>::value> class __unordered_map_equal : private _Pred { @@ -807,6 +809,7 @@ typedef const value_type& const_reference; static_assert((is_same::value), "Invalid allocator::value_type"); + static_assert(sizeof(__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(0)), ""); private: typedef __hash_value_type __value_type; @@ -1556,6 +1559,7 @@ typedef const value_type& const_reference; static_assert((is_same::value), "Invalid allocator::value_type"); + static_assert(sizeof(__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(0)), ""); private: typedef __hash_value_type __value_type; Index: libcxx/include/unordered_set =================================================================== --- libcxx/include/unordered_set +++ libcxx/include/unordered_set @@ -346,6 +346,7 @@ typedef const value_type& const_reference; static_assert((is_same::value), "Invalid allocator::value_type"); + static_assert(sizeof(__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), ""); private: typedef __hash_table __table; @@ -866,6 +867,7 @@ typedef const value_type& const_reference; static_assert((is_same::value), "Invalid allocator::value_type"); + static_assert(sizeof(__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), ""); private: typedef __hash_table __table; Index: libcxx/test/libcxx/containers/associative/non_const_comparator.fail.cpp =================================================================== --- libcxx/test/libcxx/containers/associative/non_const_comparator.fail.cpp +++ libcxx/test/libcxx/containers/associative/non_const_comparator.fail.cpp @@ -27,7 +27,8 @@ static_assert(!std::__invokable::value, ""); static_assert(std::__invokable::value, ""); - // expected-warning@__tree:* 4 {{the specified comparator type does not provide a const call operator}} + // expected-warning@set:* 2 {{the specified comparator type does not provide a const call operator}} + // expected-warning@map:* 2 {{the specified comparator type does not provide a const call operator}} { using C = std::set; C s; Index: libcxx/test/libcxx/containers/unord/non_const_comparator.fail.cpp =================================================================== --- libcxx/test/libcxx/containers/unord/non_const_comparator.fail.cpp +++ libcxx/test/libcxx/containers/unord/non_const_comparator.fail.cpp @@ -11,7 +11,7 @@ // REQUIRES: diagnose-if-support, verify-support // Test that libc++ generates a warning diagnostic when the container is -// provided a non-const callable comparator. +// provided a non-const callable comparator or a non-const hasher. #include #include @@ -34,8 +34,10 @@ static_assert(!std::__invokable::value, ""); static_assert(std::__invokable::value, ""); - // expected-warning@__hash_table:* 4 {{the specified comparator type does not provide a const call operator}} - // expected-warning@__hash_table:* 4 {{the specified hash functor does not provide a const call operator}} + // expected-warning@unordered_set:* 2 {{the specified comparator type does not provide a const call operator}} + // expected-warning@unordered_map:* 2 {{the specified comparator type does not provide a const call operator}} + // expected-warning@unordered_set:* 2 {{the specified hash functor does not provide a const call operator}} + // expected-warning@unordered_map:* 2 {{the specified hash functor does not provide a const call operator}} { using C = std::unordered_set;