On Thu, 18 Dec 2025, Jonathan Wakely wrote:
> The logic of the null pointer check got reversed when converting the
> std::stable_sort code for ranges::stable_sort.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/123180
> * include/bits/ranges_algo.h (__stable_sort_fn::operator()): Fix
> sense of null check. Replace typedef with alias-declaration.
> * testsuite/25_algorithms/stable_sort/123180.cc: New test.
Oops, I messed up converting the __builtin_expect to an [[unlikely]]..
I double checked the other __builtin_expect conversions (namely in
__inplace_merge) and they look correct at least. LGTM
> ---
>
> Tested x86_64-linux.
>
> libstdc++-v3/include/bits/ranges_algo.h | 4 +-
> .../25_algorithms/stable_sort/123180.cc | 66 +++++++++++++++++++
> 2 files changed, 68 insertions(+), 2 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/25_algorithms/stable_sort/123180.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h
> b/libstdc++-v3/include/bits/ranges_algo.h
> index 5c9fe627aee0..92e298633cf4 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -2709,7 +2709,7 @@ namespace ranges
> }
> # endif
>
> - typedef _Temporary_buffer<_Iter, iter_value_t<_Iter>> _TmpBuf;
> + using _TmpBuf = _Temporary_buffer<_Iter, iter_value_t<_Iter>>;
> // __stable_sort_adaptive sorts the range in two halves,
> // so the buffer only needs to fit half the range at once.
> _TmpBuf __buf(__first, ptrdiff_t((__last - __first + 1) / 2));
> @@ -2718,7 +2718,7 @@ namespace ranges
> __detail::__stable_sort_adaptive(__first,
> __first +
> _DistanceType(__buf.size()),
> __last, __buf.begin(),
> __comp_proj);
> - else if (__buf.begin()) [[unlikely]]
> + else if (__buf.begin() == nullptr) [[unlikely]]
> __detail::__inplace_stable_sort(__first, __last, __comp_proj);
> else
> __detail::__stable_sort_adaptive_resize(__first, __last,
> __buf.begin(),
> diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_sort/123180.cc
> b/libstdc++-v3/testsuite/25_algorithms/stable_sort/123180.cc
> new file mode 100644
> index 000000000000..7c56f37142e0
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/stable_sort/123180.cc
> @@ -0,0 +1,66 @@
> +// { dg-do run }
> +
> +#include <algorithm>
> +#include <new>
> +#include <cstdlib>
> +#include <cstdio>
> +
> +#if __cplusplus < 201103L
> +# define NOEXCEPT throw()
> +# define nullptr 0
> +# define THROW_BAD_ALLOC throw(std::bad_alloc)
> +#else
> +# define NOEXCEPT noexcept
> +# define THROW_BAD_ALLOC noexcept(false)
> +#endif
> +
> +std::size_t limit = -1u;
> +
> +void* operator new(std::size_t n, const std::nothrow_t&) NOEXCEPT
> +{
> + if (n > limit)
> + return NULL;
> + return std::malloc(n);
> +}
> +
> +void* operator new(std::size_t n) THROW_BAD_ALLOC
> +{
> + return std::malloc(n);
> +}
> +
> +void operator delete(void* p) NOEXCEPT { std::free(p); }
> +#ifdef __cpp_sized_deallocation
> +void operator delete(void* p, std::size_t) NOEXCEPT { std::free(p); }
> +#endif
> +
> +void
> +test_stl_algo()
> +{
> + int a[] = { 4, 6, 2, 1, 7, 9, 2, 2, 2, 8 };
> + limit = -1u;
> + std::stable_sort(a, a+10); // gets as much memory as it needs
> + limit = 2 * sizeof(int);
> + std::stable_sort(a, a+10); // only gets memory for two ints
> + limit = 0;
> + std::stable_sort(a, a+10); // gets no memory
> +}
> +
> +void
> +test_ranges_algo()
> +{
> +#if __cplusplus >= 202002L
> + int a[] = { 4, 6, 2, 1, 7, 9, 2, 2, 2, 8 };
> + limit = -1u;
> + std::ranges::stable_sort(a); // gets as much memory as it needs
> + limit = 2 * sizeof(int);
> + std::ranges::stable_sort(a); // only gets memory for two ints
> + limit = 0;
> + std::ranges::stable_sort(a); // gets no memory
> +#endif
> +}
> +
> +int main()
> +{
> + test_stl_algo();
> + test_ranges_algo();
> +}
> --
> 2.52.0
>
>