]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Enforce Mandates: for Boyer-Moore searchers
authorJonathan Wakely <jwakely@redhat.com>
Fri, 5 Sep 2025 10:44:57 +0000 (11:44 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 10 Sep 2025 22:24:10 +0000 (23:24 +0100)
C++17 has a 'Requires:' precondition that the two random access iterator
types have the same value type. In C++20 that is a 'Mandates:'
requirement which we must diagnose.

Although we could diagnose it in C++17, that might be a breaking change
for any users relying on it today. Also I am lazy and wanted to use
C++20's std::iter_value_t for the checks. So this only enforces the
requirement for C++20 and later.

libstdc++-v3/ChangeLog:

* include/std/functional (boyer_moore_searcher::operator()): Add
static_assert.
(boyer_moore_horspool_searcher::operator()): Likewise.
* testsuite/20_util/function_objects/121782.cc: New test.

libstdc++-v3/include/std/functional
libstdc++-v3/testsuite/20_util/function_objects/121782.cc [new file with mode: 0644]

index 5b329daf184ea79485a6a37f36d877ab7ad97cdf..bf40995659d15686a320b99e9d0555879a10ac51 100644 (file)
@@ -1256,6 +1256,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        operator()(_RandomAccessIterator2 __first,
                   _RandomAccessIterator2 __last) const
        {
+#ifdef __glibcxx_concepts // >= C++20
+         // Value types must be the same for hash function and predicate
+         // to give consistent results for lookup in the map.
+         static_assert(is_same_v<iter_value_t<_RAIter>,
+                                 iter_value_t<_RandomAccessIterator2>>);
+#endif
          const auto& __pred = this->_M_pred();
          auto __patlen = _M_pat_end - _M_pat;
          if (__patlen == 0)
@@ -1317,6 +1323,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator()(_RandomAccessIterator2 __first,
               _RandomAccessIterator2 __last) const
     {
+#ifdef __glibcxx_concepts // >= C++20
+      // Value types must be the same for hash function and predicate
+      // to give consistent results for lookup in the map.
+      static_assert(is_same_v<iter_value_t<_RAIter>,
+                             iter_value_t<_RandomAccessIterator2>>);
+#endif
       auto __patlen = _M_pat_end - _M_pat;
       if (__patlen == 0)
        return std::make_pair(__first, __first);
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/121782.cc b/libstdc++-v3/testsuite/20_util/function_objects/121782.cc
new file mode 100644 (file)
index 0000000..f18fb1e
--- /dev/null
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++17 } }
+// libstdc++/121782
+// Missing Mandates for operator() of std::boyer_moore_[horspool]_searcher
+
+// N.B. we only enforce this for C++20 and later.
+// { dg-error "static assertion failed" "" { target c++20 } 0 }
+
+#include <algorithm>
+#include <functional>
+#include <testsuite_iterators.h>
+
+template<typename T>
+using Range = __gnu_test::random_access_container<T>;
+
+void
+test_bm(Range<char> needle, Range<unsigned char> haystack)
+{
+  std::boyer_moore_searcher s(needle.begin(), needle.end());
+  (void) std::search(haystack.begin(), haystack.end(), s); // { dg-error "here" "" { target c++20 } }
+  // { dg-error "'char' is not the same as 'unsigned char'" "" { target c++20 } 0 }
+}
+
+void
+test_bmh(Range<char> needle, Range<signed char> haystack)
+{
+  std::boyer_moore_horspool_searcher s(needle.begin(), needle.end());
+  (void) std::search(haystack.begin(), haystack.end(), s); // { dg-error "here" "" { target c++20 } }
+  // { dg-error "'char' is not the same as 'signed char'" "" { target c++20 } 0 }
+}
+