PR libstdc++/85965 move is_invocable assertions again
authorJonathan Wakely <jwakely@redhat.com>
Fri, 17 May 2019 14:13:32 +0000 (15:13 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 17 May 2019 14:13:32 +0000 (15:13 +0100)
This is another attempt to reduce how often the assertions are
evaluated, so that code which doesn't try to use the function objects
doesn't need them to be invocable.

For _Rb_tree we access the _M_key_compare object directly, so can't put
the assertions in an accessor function for it. However, every invocation
of _M_key_compare is accompanied by a use of _S_key, so the assertions
can be put in there.  For _Hashtable there are member functions that are
consistently used to obtain a hash code or test for equality, so the
assertions can go in those members.

PR libstdc++/85965
* include/bits/hashtable.h (_Hashtable::~_Hashtable()): Remove static
assertions from the destructor.
* include/bits/hashtable_policy.h (_Hash_code_base::_M_hash_code):
Move static_assert for hash function to here.
(_Hash_table_base::_M_equals): Move static_assert for equality
predicate to here.
* include/bits/stl_tree.h (_Rb_tree::_S_value(_Const_Link_type)):
Remove.
(_Rb_tree::_S_key(_Const_Link_type)): Move assertions here. Access
the value directly instead of calling _S_value.
(_Rb_tree::_S_value(_Const_Base_ptr)): Remove.
(_Rb_tree::_S_key(_Const_Base_ptr)): Do downcast and forward to
_S_key(_Const_Link_type).
* testsuite/23_containers/set/85965.cc: Check construction,
destruction, assignment and size() do not trigger the assertions.
* testsuite/23_containers/unordered_set/85965.cc: Likewise.
* testsuite/23_containers/map/48101_neg.cc: Call find and adjust
expected errors.
* testsuite/23_containers/multimap/48101_neg.cc: Likewise.
* testsuite/23_containers/multiset/48101_neg.cc: Likewise.
* testsuite/23_containers/set/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_map/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_set/48101_neg.cc: Likewise.

From-SVN: r271323

14 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/hashtable.h
libstdc++-v3/include/bits/hashtable_policy.h
libstdc++-v3/include/bits/stl_tree.h
libstdc++-v3/testsuite/23_containers/map/48101_neg.cc
libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc
libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc
libstdc++-v3/testsuite/23_containers/set/48101_neg.cc
libstdc++-v3/testsuite/23_containers/set/85965.cc
libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc
libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc
libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc
libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc

index 2820b46167be313411b56377cf842bc254ea072c..6e404b228e584bf0adca9b8c8bb8869d1752a635 100644 (file)
@@ -1,5 +1,32 @@
 2019-05-17  Jonathan Wakely  <jwakely@redhat.com>
 
+       PR libstdc++/85965
+       * include/bits/hashtable.h (_Hashtable::~_Hashtable()): Remove static
+       assertions from the destructor.
+       * include/bits/hashtable_policy.h (_Hash_code_base::_M_hash_code):
+       Move static_assert for hash function to here.
+       (_Hash_table_base::_M_equals): Move static_assert for equality
+       predicate to here.
+       * include/bits/stl_tree.h (_Rb_tree::_S_value(_Const_Link_type)):
+       Remove.
+       (_Rb_tree::_S_key(_Const_Link_type)): Move assertions here. Access
+       the value directly instead of calling _S_value.
+       (_Rb_tree::_S_value(_Const_Base_ptr)): Remove.
+       (_Rb_tree::_S_key(_Const_Base_ptr)): Do downcast and forward to
+       _S_key(_Const_Link_type).
+       * testsuite/23_containers/set/85965.cc: Check construction,
+       destruction, assignment and size() do not trigger the assertions.
+       * testsuite/23_containers/unordered_set/85965.cc: Likewise.
+       * testsuite/23_containers/map/48101_neg.cc: Call find and adjust
+       expected errors.
+       * testsuite/23_containers/multimap/48101_neg.cc: Likewise.
+       * testsuite/23_containers/multiset/48101_neg.cc: Likewise.
+       * testsuite/23_containers/set/48101_neg.cc: Likewise.
+       * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise.
+       * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
+       * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
+       * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise.
+
        * include/bits/invoke.h [__cplusplus < 201703L] (__invoke_r<void>):
        Use _GLIBCXX14_CONSTEXPR because void functions cannot be constexpr
        in C++11.
index 2e8aeb7e4d4147962b735d87b03bd8d897f6c12e..ab24b5bb537a4d934aed3b54fe66764a8fa3e1fd 100644 (file)
@@ -1351,12 +1351,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       clear();
       _M_deallocate_buckets();
-
-      static_assert(__is_invocable<const _H1&, const _Key&>{},
-         "hash function must be invocable with an argument of key type");
-      static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
-         "key equality predicate must be invocable with two arguments of "
-         "key type");
     }
 
   template<typename _Key, typename _Value,
index 86589e9a2d68da04332ee5957957dbfd020da82c..a4d2a97f4f31510058285885e33ebbd81b29e32e 100644 (file)
@@ -1283,7 +1283,11 @@ namespace __detail
 
       __hash_code
       _M_hash_code(const _Key& __k) const
-      { return _M_h1()(__k); }
+      {
+       static_assert(__is_invocable<const _H1&, const _Key&>{},
+           "hash function must be invocable with an argument of key type");
+       return _M_h1()(__k);
+      }
 
       std::size_t
       _M_bucket_index(const _Key&, __hash_code __c, std::size_t __n) const
@@ -1363,7 +1367,11 @@ namespace __detail
 
       __hash_code
       _M_hash_code(const _Key& __k) const
-      { return _M_h1()(__k); }
+      {
+       static_assert(__is_invocable<const _H1&, const _Key&>{},
+           "hash function must be invocable with an argument of key type");
+       return _M_h1()(__k);
+      }
 
       std::size_t
       _M_bucket_index(const _Key&, __hash_code __c,
@@ -1783,6 +1791,9 @@ namespace __detail
     bool
     _M_equals(const _Key& __k, __hash_code __c, __node_type* __n) const
     {
+      static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
+         "key equality predicate must be invocable with two arguments of "
+         "key type");
       return _Equal_hash_code<__node_type>::_S_equals(__c, *__n)
        && _M_eq()(__k, this->_M_extract()(__n->_M_v()));
     }
index 00e4a0cccbf14d3ac92532c1c617a7db16857fe3..940155d8a3282ae5e22a23b59670bb0c7a21f2a1 100644 (file)
@@ -759,13 +759,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_end() const _GLIBCXX_NOEXCEPT
       { return &this->_M_impl._M_header; }
 
-      static const_reference
-      _S_value(_Const_Link_type __x)
-      { return *__x->_M_valptr(); }
-
       static const _Key&
       _S_key(_Const_Link_type __x)
-      { return _KeyOfValue()(_S_value(__x)); }
+      {
+#if __cplusplus >= 201103L
+       // If we're asking for the key we're presumably using the comparison
+       // object, and so this is a good place to sanity check it.
+       static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
+                     "comparison object must be invocable "
+                     "with two arguments of key type");
+# if __cplusplus >= 201703L
+       // _GLIBCXX_RESOLVE_LIB_DEFECTS
+       // 2542. Missing const requirements for associative containers
+       if constexpr (__is_invocable<_Compare&, const _Key&, const _Key&>{})
+         static_assert(
+             is_invocable_v<const _Compare&, const _Key&, const _Key&>,
+             "comparison object must be invocable as const");
+# endif // C++17
+#endif // C++11
+
+       return _KeyOfValue()(*__x->_M_valptr());
+      }
 
       static _Link_type
       _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
@@ -783,13 +797,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
       { return static_cast<_Const_Link_type>(__x->_M_right); }
 
-      static const_reference
-      _S_value(_Const_Base_ptr __x)
-      { return *static_cast<_Const_Link_type>(__x)->_M_valptr(); }
-
       static const _Key&
       _S_key(_Const_Base_ptr __x)
-      { return _KeyOfValue()(_S_value(__x)); }
+      { return _S_key(static_cast<_Const_Link_type>(__x)); }
 
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
@@ -974,21 +984,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
       ~_Rb_tree() _GLIBCXX_NOEXCEPT
-      {
-       _M_erase(_M_begin());
-
-#if __cplusplus >= 201103L
-       static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
-                     "comparison object must be invocable "
-                     "with two arguments of key type");
-# if __cplusplus >= 201703L
-      // _GLIBCXX_RESOLVE_LIB_DEFECTS
-      // 2542. Missing const requirements for associative containers
-       static_assert(is_invocable_v<const _Compare&, const _Key&, const _Key&>,
-                     "comparison object must be invocable as const");
-# endif // C++17
-#endif // C++11
-      }
+      { _M_erase(_M_begin()); }
 
       _Rb_tree&
       operator=(const _Rb_tree& __x);
index 355222058ffd533a21dd89c3310ec6d0043057ee..8a7429c85a8be4d28e943a05442050e9199b4b90 100644 (file)
@@ -24,9 +24,13 @@ void
 test01()
 {
   std::map<int, int, std::less<int*>> c;
+  c.find(1);  // { dg-error "here" }
   std::map<int, int, std::allocator<int>> c2;
+  c2.find(2); // { dg-error "here" }
 }
 
 // { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 }
 // { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 }
 // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
+// { dg-prune-output "no match for call" }
+// { dg-prune-output "invalid conversion" }
index 5f53ccee168ec943dc90b75e64a63e03709e43ee..7bd56cc9c7315dff4f1f426077dc41d31c88e502 100644 (file)
@@ -24,9 +24,13 @@ void
 test01()
 {
   std::multimap<int, int, std::less<int*>> c;
+  c.find(1);  // { dg-error "here" }
   std::multimap<int, int, std::allocator<int>> c2;
+  c2.find(2); // { dg-error "here" }
 }
 
 // { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 }
 // { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 }
 // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
+// { dg-prune-output "no match for call" }
+// { dg-prune-output "invalid conversion" }
index 71b90e80951c64cf8323515f37834bba4a805511..f13aa098976fe0d4e61b24ed4f412e2349876f9b 100644 (file)
@@ -24,9 +24,12 @@ test01()
 {
   std::multiset<const int> c;             // { dg-error "here" }
   std::multiset<int, std::less<long*>> c2;
+  c2.find(2);                             // { dg-error "here" }
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
 // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "std::allocator<.* has no member named " }
 // { dg-prune-output "must have the same value_type as its allocator" }
+// { dg-prune-output "no match for call" }
+// { dg-prune-output "invalid conversion" }
index e58c0627eb89b9bf90e1b0ac88be15c152c65911..4ede0421d903b09655155142c5e7379658ca019a 100644 (file)
@@ -24,9 +24,12 @@ test01()
 {
   std::set<const int> c;             // { dg-error "here" }
   std::set<int, std::less<long*>> c2;
+  c2.find(2);                             // { dg-error "here" }
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
 // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "std::allocator<.* has no member named " }
 // { dg-prune-output "must have the same value_type as its allocator" }
+// { dg-prune-output "no match for call" }
+// { dg-prune-output "invalid conversion" }
index 54d501f6c4fe4d4c1f011f98bc0fd3a48cdbf3d3..7d8f2167519f3fb0eea4f8e3bda505eb32d157ac 100644 (file)
@@ -27,3 +27,12 @@ struct Foo
   // PR libstdc++/85965
   std::set<Derived*, std::less<Base*>> s;
 };
+
+std::size_t
+test01(std::set<Derived*, std::less<Base*>> s)
+{
+  // these operations should not require the comparison object
+  auto copy = s;
+  copy = s;
+  return s.size();
+}
index 6c3092554f3dc9e498627a7926b61eb6bed310cf..8d823dfa47641e296a88b2de3e25c01d9826f6d2 100644 (file)
@@ -24,8 +24,10 @@ test01()
 {
   using namespace std;
   unordered_map<int, int, equal_to<int>, hash<int>> c2;
+  c2.find(2); // { dg-error "here" }
 }
 
 // { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
 // { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "use of deleted function" }
+// { dg-prune-output "no match for call" }
index f5de313a8f172edbe54a834a2431838a24f21a1b..a81615b3607c3e3075bcec3898438ec8f766aeba 100644 (file)
@@ -24,8 +24,10 @@ test01()
 {
   using namespace std;
   unordered_multimap<int, int, equal_to<int>, hash<int>> c2;
+  c2.find(2); // { dg-error "here" }
 }
 
 // { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
 // { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "use of deleted function" }
+// { dg-prune-output "no match for call" }
index d4e479aaf974d7e6ed25053f2de229ea57683808..03ddb898d6cec3c93abe89f3248e1ae9e8b99ae3 100644 (file)
@@ -25,6 +25,7 @@ test01()
   using namespace std;
   unordered_multiset<const int, hash<int>> c;          // { dg-error "here" }
   unordered_multiset<int, equal_to<int>, hash<int>> c2;
+  c2.find(2);                                      // { dg-error "here" }
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
@@ -32,3 +33,4 @@ test01()
 // { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "use of deleted function" }
 // { dg-prune-output "must have the same value_type as its allocator" }
+// { dg-prune-output "no match for call" }
index c7e27c536905ba60d1c5f280b79c42d6c7fc69f3..e79d3769248551ad8609527b73c712770b24bfe9 100644 (file)
@@ -25,6 +25,7 @@ test01()
   using namespace std;
   unordered_set<const int, hash<int>> c;           // { dg-error "here" }
   unordered_set<int, equal_to<int>, hash<int>> c2;
+  c2.find(2);                                      // { dg-error "here" }
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
@@ -32,3 +33,4 @@ test01()
 // { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
 // { dg-prune-output "use of deleted function" }
 // { dg-prune-output "must have the same value_type as its allocator" }
+// { dg-prune-output "no match for call" }
index 8b90b369901357879739719f1b8a060fbb30b4e5..8c48fa2a978157a5ddb4bb9e706e61fd43f6929c 100644 (file)
@@ -27,3 +27,12 @@ struct Foo
   // PR libstdc++/85965
   std::unordered_set<Derived*, std::equal_to<Base*>, std::hash<Base*>> u;
 };
+
+std::size_t
+test01(std::unordered_set<Derived*, std::equal_to<Base*>, std::hash<Base*>> s)
+{
+  // these operations should not require the comparison object
+  auto copy = s;
+  copy = s;
+  return s.size();
+}