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?

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

Reply via email to