https://gcc.gnu.org/g:a559f1423cd72c40a9467429f0fcb14435bf7dcf

commit r16-3754-ga559f1423cd72c40a9467429f0fcb14435bf7dcf
Author: Jonathan Wakely <[email protected]>
Date:   Fri Sep 5 11:44:57 2025 +0100

    libstdc++: Enforce Mandates: for Boyer-Moore searchers
    
    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.

Diff:
---
 libstdc++-v3/include/std/functional                | 12 +++++++++
 .../testsuite/20_util/function_objects/121782.cc   | 30 ++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/libstdc++-v3/include/std/functional 
b/libstdc++-v3/include/std/functional
index 5b329daf184e..bf40995659d1 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -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
index 000000000000..f18fb1ef25cc
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/121782.cc
@@ -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 }
+}
+

Reply via email to