[libcxx] r291969 - Diagnose non-const-callable hash functions and comparators

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 13 14:42:53 PST 2017


Author: ericwf
Date: Fri Jan 13 16:42:53 2017
New Revision: 291969

URL: http://llvm.org/viewvc/llvm-project?rev=291969&view=rev
Log:
Diagnose non-const-callable hash functions and comparators

Added:
    libcxx/trunk/test/libcxx/containers/unord/non_const_comparator.fail.cpp
Modified:
    libcxx/trunk/include/__hash_table
    libcxx/trunk/include/__tree
    libcxx/trunk/include/type_traits
    libcxx/trunk/include/unordered_map
    libcxx/trunk/test/libcxx/containers/associative/non_const_comparator.fail.cpp

Modified: libcxx/trunk/include/__hash_table
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__hash_table?rev=291969&r1=291968&r2=291969&view=diff
==============================================================================
--- libcxx/trunk/include/__hash_table (original)
+++ libcxx/trunk/include/__hash_table Fri Jan 13 16:42:53 2017
@@ -18,6 +18,7 @@
 #include <algorithm>
 #include <cmath>
 #include <utility>
+#include <type_traits>
 
 #include <__undef_min_max>
 
@@ -38,6 +39,15 @@ template <class _Key, class _Tp>
 struct __hash_value_type;
 #endif
 
+template <class _Key, class _Cp, class _Hash,
+          bool =  is_empty<_Hash>::value && !__libcpp_is_final<_Hash>::value>
+class __unordered_map_hasher;
+
+template <class _Key, class _Cp, class _Pred,
+          bool = is_empty<_Pred>::value && !__libcpp_is_final<_Pred>::value
+         >
+class __unordered_map_equal;
+
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Tp>
 struct __is_hash_value_type_imp : false_type {};
@@ -856,6 +866,29 @@ public:
     template <class> friend class __hash_map_node_destructor;
 };
 
+
+#ifndef _LIBCPP_CXX03_LANG
+template <class _Key, class _Hash, class _Equal, class _Alloc>
+struct __diagnose_hash_table_helper {
+  static constexpr bool __trigger_diagnostics()
+  _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Hash const&, _Key const&>::value,
+    "the specified hash functor does not provide a const call operator")
+  _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Equal const&, _Key const&, _Key const&>::value,
+    "the specified comparator type does not provide a const call operator")
+  { return true; }
+};
+
+template <class _Key, class _Value, class _Hash, class _Equal, class _Alloc>
+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 <class _Tp, class _Hash, class _Equal, class _Alloc>
 class __hash_table
 {
@@ -1453,6 +1486,9 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>
                  "Predicate must be copy-constructible.");
     static_assert((is_copy_constructible<hasher>::value),
                  "Hasher must be copy-constructible.");
+#ifndef _LIBCPP_CXX03_LANG
+    static_assert(__diagnose_hash_table_helper<_Tp, _Hash, _Equal, _Alloc>::__trigger_diagnostics(), "");
+#endif
     __deallocate_node(__p1_.first().__next_);
 #if _LIBCPP_DEBUG_LEVEL >= 2
     __get_db()->__erase_c(this);

Modified: libcxx/trunk/include/__tree
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__tree?rev=291969&r1=291968&r2=291969&view=diff
==============================================================================
--- libcxx/trunk/include/__tree (original)
+++ libcxx/trunk/include/__tree Fri Jan 13 16:42:53 2017
@@ -963,7 +963,7 @@ private:
 template <class _Tp, class _Compare, class _Allocator>
 struct __diagnose_tree_helper {
   static constexpr bool __trigger_diagnostics()
-      _LIBCPP_DIAGNOSE_WARNING(!__is_const_comparable<_Compare, _Tp>::value,
+      _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Compare const&, _Tp const&, _Tp const&>::value,
             "the specified comparator type does not provide a const call operator")
   { return true; }
 };
@@ -973,15 +973,10 @@ struct __diagnose_tree_helper<
     __value_type<_Key, _Value>,
     __map_value_compare<_Key, __value_type<_Key, _Value>, _KeyComp>,
     _Alloc
->
+> : __diagnose_tree_helper<_Key, _KeyComp, _Alloc>
 {
-  static constexpr bool __trigger_diagnostics()
-      _LIBCPP_DIAGNOSE_WARNING(!__is_const_comparable<_KeyComp, _Key>::value,
-            "the specified comparator type does not provide a const call operator")
-  { return true; }
 };
-
-#endif
+#endif // !_LIBCPP_CXX03_LANG
 
 template <class _Tp, class _Compare, class _Allocator>
 class __tree

Modified: libcxx/trunk/include/type_traits
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=291969&r1=291968&r2=291969&view=diff
==============================================================================
--- libcxx/trunk/include/type_traits (original)
+++ libcxx/trunk/include/type_traits Fri Jan 13 16:42:53 2017
@@ -4715,15 +4715,6 @@ struct __can_extract_map_key<_ValTy, _Ke
 
 #endif
 
-template <class _Comp, class _ValueType, class = void>
-struct __is_const_comparable : false_type {};
-
-template <class _Comp, class _ValueType>
-struct __is_const_comparable<_Comp, _ValueType, typename __void_t<
-    decltype(_VSTD::declval<_Comp const&>()(_VSTD::declval<_ValueType const&>(),
-                                            _VSTD::declval<_ValueType const&>()))
-  >::type> : true_type {};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif  // _LIBCPP_TYPE_TRAITS

Modified: libcxx/trunk/include/unordered_map
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/unordered_map?rev=291969&r1=291968&r2=291969&view=diff
==============================================================================
--- libcxx/trunk/include/unordered_map (original)
+++ libcxx/trunk/include/unordered_map Fri Jan 13 16:42:53 2017
@@ -379,9 +379,7 @@ template <class Key, class T, class Hash
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Key, class _Cp, class _Hash,
-          bool = is_empty<_Hash>::value && !__libcpp_is_final<_Hash>::value
-         >
+template <class _Key, class _Cp, class _Hash, bool _IsEmpty>
 class __unordered_map_hasher
     : private _Hash
 {
@@ -449,9 +447,7 @@ swap(__unordered_map_hasher<_Key, _Cp, _
     __x.swap(__y);
 }
 
-template <class _Key, class _Cp, class _Pred,
-          bool = is_empty<_Pred>::value && !__libcpp_is_final<_Pred>::value
-         >
+template <class _Key, class _Cp, class _Pred, bool _IsEmpty>
 class __unordered_map_equal
     : private _Pred
 {

Modified: libcxx/trunk/test/libcxx/containers/associative/non_const_comparator.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/containers/associative/non_const_comparator.fail.cpp?rev=291969&r1=291968&r2=291969&view=diff
==============================================================================
--- libcxx/trunk/test/libcxx/containers/associative/non_const_comparator.fail.cpp (original)
+++ libcxx/trunk/test/libcxx/containers/associative/non_const_comparator.fail.cpp Fri Jan 13 16:42:53 2017
@@ -24,7 +24,9 @@ struct BadCompare {
 };
 
 int main() {
-  static_assert(!std::__is_const_comparable<BadCompare, int>::value, "");
+  static_assert(!std::__invokable<BadCompare const&, int const&, int const&>::value, "");
+  static_assert(std::__invokable<BadCompare&, int const&, int const&>::value, "");
+
   // expected-warning at __tree:* 4 {{the specified comparator type does not provide a const call operator}}
   {
     using C = std::set<int, BadCompare>;

Added: libcxx/trunk/test/libcxx/containers/unord/non_const_comparator.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/containers/unord/non_const_comparator.fail.cpp?rev=291969&view=auto
==============================================================================
--- libcxx/trunk/test/libcxx/containers/unord/non_const_comparator.fail.cpp (added)
+++ libcxx/trunk/test/libcxx/containers/unord/non_const_comparator.fail.cpp Fri Jan 13 16:42:53 2017
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+// REQUIRES: diagnose-if-support, verify-support
+
+// Test that libc++ generates a warning diagnostic when the container is
+// provided a non-const callable comparator.
+
+#include <unordered_set>
+#include <unordered_map>
+
+struct BadHash {
+  template <class T>
+  size_t operator()(T const& t) {
+    return std::hash<T>{}(t);
+  }
+};
+
+struct BadEqual {
+  template <class T, class U>
+  bool operator()(T const& t, U const& u) {
+    return t == u;
+  }
+};
+
+int main() {
+  static_assert(!std::__invokable<BadEqual const&, int const&, int const&>::value, "");
+  static_assert(std::__invokable<BadEqual&, int const&, int const&>::value, "");
+
+  // expected-warning at __hash_table:* 4 {{the specified comparator type does not provide a const call operator}}
+  // expected-warning at __hash_table:* 4 {{the specified hash functor does not provide a const call operator}}
+
+  {
+    using C = std::unordered_set<int, BadHash, BadEqual>;
+    C s;
+  }
+  {
+    using C = std::unordered_multiset<long, BadHash, BadEqual>;
+    C s;
+  }
+  {
+    using C = std::unordered_map<int, int, BadHash, BadEqual>;
+    C s;
+  }
+  {
+    using C = std::unordered_multimap<long, int, BadHash, BadEqual>;
+    C s;
+  }
+}




More information about the cfe-commits mailing list