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 } +} +
