Okay. Thank you!
вт, 21 июл. 2026 г. в 15:36, Jonathan Wakely <[email protected]>: > On Tue, 21 Jul 2026 at 13:30, Jonathan Wakely <[email protected]> wrote: > > > > On Tue, 21 Jul 2026 at 12:42, Ян Чуркин <[email protected]> wrote: > > > > > > Hi. > > > > > > I wanted to follow up on this patch. Could you let me know whether it > is still under consideration for merging or if the decision has been made > not to merge it? > > > > I plan to merge it. > > The mismatch test needs a fix for C++98 compatibility though, as it uses > 'auto'. > The test for __cplusplus >= 201402L should use > __cpp_lib_robust_nonmodifying_seq_ops instead. > I'll take care of those. > > > > > > > > > > > Thank you! > > > > > > > > > пт, 26 июн. 2026 г. в 22:49, Ян Чуркин <[email protected]>: > > >> > > >> Thank you! > > >> > > >> I sent the updated patch with the code comments and copyrights > removed. In both tests I added a note that the tested case is UB handled as > QoI. > > >> > > >> пт, 26 июн. 2026 г. в 22:47, Yan Churkin <[email protected]>: > > >>> > > >>> std::__find_if, std::__mismatch and std::__push_heap drove their > loops > > >>> with a condition of the form > > >>> > > >>> while (__first != __last && PREDICATE_CALL(...)) > > >>> > > >>> If the predicate/comparator result type has an ADL-reachable > > >>> operator&&(bool, T), overload resolution selects that user-defined > > >>> operator&& for the loop condition. It does not short-circuit, so the > > >>> operand that dereferences *__first is evaluated even when > > >>> __first == __last, dereferencing the past-the-end iterator. > > >>> > > >>> Such a result type does not model boolean-testable, so this is > undefined > > >>> behaviour and not a conformance issue. Handle it anyway as a QoI > > >>> extension, consistent with std::equal, std::binary_search and > > >>> std::__partition, by forcing the predicate result to bool so the > > >>> built-in && is used. This has no effect on well-behaved predicates; > the > > >>> ranges:: versions are unaffected because their wrappers already > return > > >>> bool. > > >>> > > >>> PR libstdc++/125981 > > >>> > > >>> libstdc++-v3/ChangeLog: > > >>> > > >>> * include/bits/stl_algobase.h (__find_if): Force the > predicate > > >>> result to bool so an ADL-found, non-short-circuiting > operator&& / > > >>> operator! cannot dereference the past-the-end iterator. > > >>> (__mismatch): Likewise for both overloads. > > >>> * include/bits/stl_heap.h (__push_heap): Likewise for the > > >>> comparator result. > > >>> * testsuite/25_algorithms/find_if/overloaded_logical_ops.cc: > New test. > > >>> * > testsuite/25_algorithms/mismatch/overloaded_logical_ops.cc: New test. > > >>> > > >>> Signed-off-by: Yan Churkin <[email protected]> > > >>> --- > > >>> libstdc++-v3/include/bits/stl_algobase.h | 6 +-- > > >>> libstdc++-v3/include/bits/stl_heap.h | 3 +- > > >>> .../find_if/overloaded_logical_ops.cc | 50 +++++++++++++++++ > > >>> .../mismatch/overloaded_logical_ops.cc | 53 > +++++++++++++++++++ > > >>> 4 files changed, 108 insertions(+), 4 deletions(-) > > >>> create mode 100644 > libstdc++-v3/testsuite/25_algorithms/find_if/overloaded_logical_ops.cc > > >>> create mode 100644 > libstdc++-v3/testsuite/25_algorithms/mismatch/overloaded_logical_ops.cc > > >>> > > >>> diff --git a/libstdc++-v3/include/bits/stl_algobase.h > b/libstdc++-v3/include/bits/stl_algobase.h > > >>> index 1350736a8..33b117781 100644 > > >>> --- a/libstdc++-v3/include/bits/stl_algobase.h > > >>> +++ b/libstdc++-v3/include/bits/stl_algobase.h > > >>> @@ -1930,7 +1930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO > > >>> __mismatch(_InputIterator1 __first1, _InputIterator1 __last1, > > >>> _InputIterator2 __first2, _BinaryPredicate > __binary_pred) > > >>> { > > >>> - while (__first1 != __last1 && __binary_pred(*__first1, > *__first2)) > > >>> + while (__first1 != __last1 && bool(__binary_pred(*__first1, > *__first2))) > > >>> { > > >>> ++__first1; > > >>> ++__first2; > > >>> @@ -2011,7 +2011,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO > > >>> _BinaryPredicate __binary_pred) > > >>> { > > >>> while (__first1 != __last1 && __first2 != __last2 > > >>> - && __binary_pred(*__first1, *__first2)) > > >>> + && bool(__binary_pred(*__first1, *__first2))) > > >>> { > > >>> ++__first1; > > >>> ++__first2; > > >>> @@ -2097,7 +2097,7 @@ _GLIBCXX_END_NAMESPACE_ALGO > > >>> __find_if(_Iterator __first, _Iterator __last, _Predicate > __pred) > > >>> { > > >>> #pragma GCC unroll 4 > > >>> - while (__first != __last && !__pred(*__first)) > > >>> + while (__first != __last && !bool(__pred(*__first))) > > >>> ++__first; > > >>> return __first; > > >>> } > > >>> diff --git a/libstdc++-v3/include/bits/stl_heap.h > b/libstdc++-v3/include/bits/stl_heap.h > > >>> index 8c5c5df52..3c26e6a53 100644 > > >>> --- a/libstdc++-v3/include/bits/stl_heap.h > > >>> +++ b/libstdc++-v3/include/bits/stl_heap.h > > >>> @@ -143,7 +143,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > >>> _Compare& __comp) > > >>> { > > >>> _Distance __parent = (__holeIndex - 1) / 2; > > >>> - while (__holeIndex > __topIndex && __comp(*(__first + > __parent), __value)) > > >>> + while (__holeIndex > __topIndex > > >>> + && bool(__comp(*(__first + __parent), __value))) > > >>> { > > >>> *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + > __parent)); > > >>> __holeIndex = __parent; > > >>> diff --git > a/libstdc++-v3/testsuite/25_algorithms/find_if/overloaded_logical_ops.cc > b/libstdc++-v3/testsuite/25_algorithms/find_if/overloaded_logical_ops.cc > > >>> new file mode 100644 > > >>> index 000000000..a26f4d557 > > >>> --- /dev/null > > >>> +++ > b/libstdc++-v3/testsuite/25_algorithms/find_if/overloaded_logical_ops.cc > > >>> @@ -0,0 +1,50 @@ > > >>> +// The predicate's result type does not model boolean-testable (it > has an > > >>> +// ADL-reachable operator&& and operator!), so this is undefined > behaviour. > > >>> +// libstdc++ supports it as a QoI extension: std::find_if does not > evaluate > > >>> +// the predicate on, or dereference, the past-the-end iterator. > > >>> + > > >>> +#include <algorithm> > > >>> +#include <testsuite_hooks.h> > > >>> +#include <testsuite_iterators.h> > > >>> + > > >>> +using __gnu_test::test_container; > > >>> +using __gnu_test::forward_iterator_wrapper; > > >>> + > > >>> +int truth = 0; > > >>> + > > >>> +struct Logic > > >>> +{ > > >>> + Logic operator!() const { return Logic(); } > > >>> + operator bool() const { return truth != 0; } > > >>> +}; > > >>> + > > >>> +struct Value > > >>> +{ > > >>> + Logic operator>(Value) const { return Logic(); } > > >>> +}; > > >>> + > > >>> +bool operator&&(bool, Logic) { return false; } > > >>> + > > >>> +struct Pred > > >>> +{ > > >>> + Logic operator()(Value v) const { return v > v; } > > >>> +}; > > >>> + > > >>> +void > > >>> +test01() > > >>> +{ > > >>> + Value arr[1] = { }; > > >>> + > > >>> + test_container<Value, forward_iterator_wrapper> empty(arr, arr); > > >>> + VERIFY( std::find_if(empty.begin(), empty.end(), Pred()).ptr == > arr ); > > >>> + > > >>> + test_container<Value, forward_iterator_wrapper> con(arr, arr + 1); > > >>> + VERIFY( std::find_if(con.begin(), con.end(), Pred()).ptr == arr + > 1 ); > > >>> +} > > >>> + > > >>> +int > > >>> +main() > > >>> +{ > > >>> + test01(); > > >>> + return 0; > > >>> +} > > >>> diff --git > a/libstdc++-v3/testsuite/25_algorithms/mismatch/overloaded_logical_ops.cc > b/libstdc++-v3/testsuite/25_algorithms/mismatch/overloaded_logical_ops.cc > > >>> new file mode 100644 > > >>> index 000000000..dd52d070b > > >>> --- /dev/null > > >>> +++ > b/libstdc++-v3/testsuite/25_algorithms/mismatch/overloaded_logical_ops.cc > > >>> @@ -0,0 +1,53 @@ > > >>> +// The predicate's result type does not model boolean-testable (it > has an > > >>> +// ADL-reachable operator&& and operator!), so this is undefined > behaviour. > > >>> +// libstdc++ supports it as a QoI extension: std::mismatch does not > evaluate > > >>> +// the predicate on, or dereference, the past-the-end iterator. > > >>> + > > >>> +#include <algorithm> > > >>> +#include <testsuite_hooks.h> > > >>> +#include <testsuite_iterators.h> > > >>> + > > >>> +using __gnu_test::test_container; > > >>> +using __gnu_test::forward_iterator_wrapper; > > >>> + > > >>> +int truth = 0; > > >>> + > > >>> +struct Logic > > >>> +{ > > >>> + Logic operator!() const { return Logic(); } > > >>> + operator bool() const { return truth != 0; } > > >>> +}; > > >>> + > > >>> +struct Value { }; > > >>> + > > >>> +bool operator&&(bool, Logic) { return false; } > > >>> + > > >>> +struct Eq > > >>> +{ > > >>> + Logic operator()(Value, Value) const { return Logic(); } > > >>> +}; > > >>> + > > >>> +void > > >>> +test01() > > >>> +{ > > >>> + Value arr[1] = { }; > > >>> + > > >>> + test_container<Value, forward_iterator_wrapper> empty(arr, arr); > > >>> + test_container<Value, forward_iterator_wrapper> other(arr, arr + > 1); > > >>> + auto r = std::mismatch(empty.begin(), empty.end(), other.begin(), > Eq()); > > >>> + VERIFY( r.first.ptr == arr ); > > >>> + > > >>> +#if __cplusplus >= 201402L > > >>> + test_container<Value, forward_iterator_wrapper> e2(arr, arr); > > >>> + test_container<Value, forward_iterator_wrapper> o2(arr, arr + 1); > > >>> + auto r2 = std::mismatch(e2.begin(), e2.end(), o2.begin(), > o2.end(), Eq()); > > >>> + VERIFY( r2.first.ptr == arr ); > > >>> +#endif > > >>> +} > > >>> + > > >>> +int > > >>> +main() > > >>> +{ > > >>> + test01(); > > >>> + return 0; > > >>> +} > > >>> -- > > >>> 2.43.0 > > >>> > >
