On Wed, 14 Jan 2026 at 09:09, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Tue, Jan 13, 2026 at 5:22 PM Tomasz Kaminski <[email protected]> wrote:
>>
>>
>>
>> On Tue, Jan 13, 2026 at 5:15 PM Jonathan Wakely <[email protected]> wrote:
>>>
>>> On Tue, 13 Jan 2026 at 16:06, Tomasz Kamiński <[email protected]> wrote:
>>> >
>>> > This patch replaces uses of subscripts in heap algorithms, that where 
>>> > introduced
>>> > in r16-4100-gaaeca77a79a9a8 with dereference of advanced iterators.
>>> >
>>> > The Cpp17RandomAccessIterator requirements, allows operator[] to return 
>>> > any
>>> > type that is convertible to reference, however user-provided comparators 
>>> > are
>>> > required only to accept result of dereferencing the iterator (i.e. 
>>> > reference
>>> > directly). This is visible, when comparator defines template operator() 
>>> > for
>>> > which arguments can be detected from reference (which will fail on proxy) 
>>> > or
>>> > types convertible from reference (see included tests).
>>> >
>>> > For test we introduce a new proxy_random_access_iterator_wrapper iterator
>>> > in testsuite_iterators.h, that returns a proxy type from subscript 
>>> > operator.
>>> > This is separate type (instead of additional template argument and 
>>> > aliases),
>>> > as it used for test that work with C++98.
>>> >
>>> > libstdc++-v3/ChangeLog:
>>> >
>>> >         * include/bits/stl_heap.h (std::__is_heap_until, std::__push_heap)
>>> >         (std::__adjust_heap): Replace subscript with dereference of
>>> >         advanced iterator.
>>> >         * testsuite/util/testsuite_iterators.h 
>>> > (__gnu_test::subscript_proxy)
>>> >         (__gnu_test::proxy_random_access_iterator_wrapper): Define.
>>> >         * testsuite/25_algorithms/sort_heap/check_proxy_brackets.cc: New 
>>> > test.
>>> > ---
>>> > In future we could replace random_access_iterator_wrapper with
>>> > proxy_random_access_iterator_wrapper so it is used in all test. However,
>>> > we should think about contingous_iterator_wrapper, and this is much
>>> > larger change.
>>>
>>> Ah OK. I was hoping we would change the existing class template
>>> because that would mean the new proxy would be used by all the
>>> existing tests that use random_access_iterator_wrapper. I probably
>>> won't remember to use the new type when I write tests :-)
>>
>> I think we should do that in the future (soonish), after double checking
>> the contiguous iterator requirement, as I was not sure if it can return a 
>> proxy,
>> I wanted to deliver the fix quickly, so limited scope of the changes.
>
> Unfortunately I think  we need to keep both version of iterators, as
> proxy_random_access_iterator is Cpp17RandomAccessIterator but does not
> satisfy random_access_iterator concept 
> (https://eel.is/c++draft/iterator.concept.random.access)
> due:
>   { j[n] } -> same_as<iter_reference_t<I>>;
>
> This is interesting, as random_access_iterator allows using proxy as reference
> (so in operator* and operator[]), but Cpp17RandomAccessIterator requires T&
> on operator*, but allows subscript on operator[].

Ugh, such "fun".

Could you add a comment to the new proxy_r_a_iterator_wrapper class
saying what you wrote above, so I don't forget and waste time trying
to merge them in future? :-)


>
>>>
>>> But I hadn't realised that it wouldn't be a drop-in replacement, so I
>>> agree we should make it a separate type for now.
>>>
>>>
>>> >
>>> > Testing on x86_64-linux. The new file passed with C++98-26 and
>>> > with/without -m32 and local test pased. OK for trunk when all test
>>> > passes.
>>>
>>> Did you test with PCH enabled? I don't think the #undef in the new
>>> test will do anything if PCH is enabled (because the library headers
>>> will be included already). But we have that #undef in other tests for
>>> heap algos (since 2007) so I guess it either works, or it's harmless.
>>
>> Just started with a copy paste from another test, and haven't removed it.
>>>
>>>
>>> OK for trunk when testing finishes - thanks for fixing it quickly.
>>>
>>> >
>>> >
>>> >  libstdc++-v3/include/bits/stl_heap.h          |   8 +-
>>> >  .../sort_heap/check_proxy_brackets.cc         |  67 ++++++
>>> >  .../testsuite/util/testsuite_iterators.h      | 198 ++++++++++++++++++
>>> >  3 files changed, 269 insertions(+), 4 deletions(-)
>>> >  create mode 100644 
>>> > libstdc++-v3/testsuite/25_algorithms/sort_heap/check_proxy_brackets.cc
>>> >
>>> > diff --git a/libstdc++-v3/include/bits/stl_heap.h 
>>> > b/libstdc++-v3/include/bits/stl_heap.h
>>> > index c64553e2571..8c5c5df5266 100644
>>> > --- a/libstdc++-v3/include/bits/stl_heap.h
>>> > +++ b/libstdc++-v3/include/bits/stl_heap.h
>>> > @@ -85,7 +85,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>> >        _Distance __parent = 0;
>>> >        for (_Distance __child = 1; __child < __n; ++__child)
>>> >         {
>>> > -         if (__comp(__first[__parent], __first[__child]))
>>> > +         if (__comp(*(__first + __parent), *(__first + __child)))
>>> >             return __child;
>>> >           if ((__child & 1) == 0)
>>> >             ++__parent;
>>> > @@ -143,7 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>> >                 _Compare& __comp)
>>> >      {
>>> >        _Distance __parent = (__holeIndex - 1) / 2;
>>> > -      while (__holeIndex > __topIndex && __comp(__first[__parent], 
>>> > __value))
>>> > +      while (__holeIndex > __topIndex && __comp(*(__first + __parent), 
>>> > __value))
>>> >         {
>>> >           *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __parent));
>>> >           __holeIndex = __parent;
>>> > @@ -233,8 +233,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>> >        while (__secondChild < (__len - 1) / 2)
>>> >         {
>>> >           __secondChild = 2 * (__secondChild + 1);
>>> > -         if (__comp(__first[__secondChild],
>>> > -                    __first[__secondChild - 1]))
>>> > +         if (__comp(*(__first + __secondChild),
>>> > +                    *(__first + _Distance(__secondChild - 1))))
>>> >             __secondChild--;
>>> >           *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + 
>>> > __secondChild));
>>> >           __holeIndex = __secondChild;
>>> > diff --git 
>>> > a/libstdc++-v3/testsuite/25_algorithms/sort_heap/check_proxy_brackets.cc 
>>> > b/libstdc++-v3/testsuite/25_algorithms/sort_heap/check_proxy_brackets.cc
>>> > new file mode 100644
>>> > index 00000000000..c556c26dfa8
>>> > --- /dev/null
>>> > +++ 
>>> > b/libstdc++-v3/testsuite/25_algorithms/sort_heap/check_proxy_brackets.cc
>>> > @@ -0,0 +1,67 @@
>>> > +// { dg-do run  }
>>> > +
>>> > +#undef _GLIBCXX_CONCEPT_CHECKS
>>> > +
>>> > +#include <algorithm>
>>> > +#include <testsuite_hooks.h>
>>> > +#include <testsuite_iterators.h>
>>> > +#include <utility>
>>> > +
>>> > +using __gnu_test::test_container;
>>> > +using __gnu_test::proxy_random_access_iterator_wrapper;
>>> > +
>>> > +typedef std::pair<int, int> V;
>>> > +typedef test_container<V, proxy_random_access_iterator_wrapper> 
>>> > Container;
>>> > +
>>> > +struct Deduced
>>> > +{
>>> > +  template<typename A, typename B>
>>> > +  bool operator()(const std::pair<A, B>& lhs,
>>> > +                 const std::pair<A, B>& rhs) const
>>> > +  { return lhs.first < rhs.first; }
>>> > +};
>>> > +
>>> > +struct Conversion
>>> > +{
>>> > +  struct First
>>> > +  {
>>> > +    First(const V& p) : v(p.first)
>>> > +    {}
>>> > +
>>> > +    int v;
>>> > +  };
>>> > +
>>> > +  bool operator()(First lhs, First rhs) const
>>> > +  { return lhs.v < rhs.v; }
>>> > +};
>>> > +
>>> > +template<typename Cmp>
>>> > +void
>>> > +test01(Cmp cmp)
>>> > +{
>>> > +  V s1[] = { V(0, 10), V(8, 18), V(6, 16), V(7, 17), V(9, 19),
>>> > +            V(5, 15), V(4, 14), V(3, 13), V(2, 12), V(1, 11) };
>>> > +  const int N = sizeof(s1) / sizeof(V);
>>> > +  Container con(s1, s1 + N);
>>> > +
>>> > +  std::make_heap(con.begin(), con.end() - std::ptrdiff_t(2), cmp);
>>> > +  std::push_heap(con.begin(), con.end() - std::ptrdiff_t(1), cmp);
>>> > +  std::push_heap(con.begin(), con.end(), cmp);
>>> > +#if __cplusplus >= 201103L
>>> > +  (void)std::is_heap_until(con.begin(), con.end(), cmp);
>>> > +#endif
>>> > +  std::sort_heap(con.begin(), con.end(), cmp);
>>> > +  for(int i = 0; i < N; ++i)
>>> > +  {
>>> > +    VERIFY( s1[i].first == i );
>>> > +    VERIFY( s1[i].second == 10 + i );
>>> > +  }
>>> > +}
>>> > +
>>> > +int
>>> > +main()
>>> > +{
>>> > +  test01(Deduced());
>>> > +  test01(Conversion());
>>> > +  return 0;
>>> > +}
>>> > diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h 
>>> > b/libstdc++-v3/testsuite/util/testsuite_iterators.h
>>> > index 95589b2626d..caede49e41b 100644
>>> > --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h
>>> > +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h
>>> > @@ -664,6 +664,204 @@ namespace __gnu_test
>>> >  #endif
>>> >
>>> >
>>> > +  template<typename T>
>>> > +  struct subscript_proxy
>>> > +  {
>>> > +    _GLIBCXX_CONSTEXPR
>>> > +    operator T&() const
>>> > +    { return *ptr; }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    subscript_proxy& operator=(const T& val)
>>> > +    {
>>> > +      *ptr = val;
>>> > +      return *this;
>>> > +    }
>>> > +
>>> > +    T* ptr;
>>> > +  };
>>> > +
>>> > +  template<typename T>
>>> > +  struct subscript_proxy<const T>
>>> > +  {
>>> > +    _GLIBCXX_CONSTEXPR
>>> > +    operator const T&() const
>>> > +    { return *ptr; }
>>> > +
>>> > +    const T* ptr;
>>> > +  };
>>> > +
>>> > +  /**
>>> > +   * @brief random_access_iterator wrapper for pointer,
>>> > +   * that returns proxy from subscript.
>>> > +   *
>>> > +   * This class takes a pointer and wraps it to provide exactly
>>> > +   * the requirements of a random_access_iterator. It should not be
>>> > +   * instantiated directly, but generated from a test_container
>>> > +   */
>>> > +  template<class T>
>>> > +  struct proxy_random_access_iterator_wrapper
>>> > +  : public bidirectional_iterator_wrapper<T>
>>> > +  {
>>> > +    typedef BoundsContainer<T> ContainerType;
>>> > +    typedef std::random_access_iterator_tag iterator_category;
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper(T* _ptr, ContainerType* 
>>> > SharedInfo_in)
>>> > +    : bidirectional_iterator_wrapper<T>(_ptr, SharedInfo_in)
>>> > +    { }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper()
>>> > +    : bidirectional_iterator_wrapper<T>()
>>> > +    { }
>>> > +
>>> > +#if __cplusplus >= 201103L
>>> > +    proxy_random_access_iterator_wrapper(
>>> > +       const proxy_random_access_iterator_wrapper&) = default;
>>> > +
>>> > +    proxy_random_access_iterator_wrapper&
>>> > +    operator=(const proxy_random_access_iterator_wrapper&) = default;
>>> > +#endif
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper&
>>> > +    operator++()
>>> > +    {
>>> > +      ITERATOR_VERIFY(this->SharedInfo && this->ptr < 
>>> > this->SharedInfo->last);
>>> > +      this->ptr++;
>>> > +      return *this;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper
>>> > +    operator++(int)
>>> > +    {
>>> > +      proxy_random_access_iterator_wrapper<T> tmp = *this;
>>> > +      ++*this;
>>> > +      return tmp;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper&
>>> > +    operator--()
>>> > +    {
>>> > +      ITERATOR_VERIFY(this->SharedInfo && this->ptr > 
>>> > this->SharedInfo->first);
>>> > +      this->ptr--;
>>> > +      return *this;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper
>>> > +    operator--(int)
>>> > +    {
>>> > +      proxy_random_access_iterator_wrapper<T> tmp = *this;
>>> > +      --*this;
>>> > +      return tmp;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper&
>>> > +    operator+=(std::ptrdiff_t n)
>>> > +    {
>>> > +      if(n > 0)
>>> > +       {
>>> > +         ITERATOR_VERIFY(n <= this->SharedInfo->last - this->ptr);
>>> > +         this->ptr += n;
>>> > +       }
>>> > +      else
>>> > +       {
>>> > +         ITERATOR_VERIFY(-n <= this->ptr - this->SharedInfo->first);
>>> > +         this->ptr += n;
>>> > +       }
>>> > +      return *this;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper&
>>> > +    operator-=(std::ptrdiff_t n)
>>> > +    { return *this += -n; }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper
>>> > +    operator-(std::ptrdiff_t n) const
>>> > +    {
>>> > +      proxy_random_access_iterator_wrapper<T> tmp = *this;
>>> > +      return tmp -= n;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    std::ptrdiff_t
>>> > +    operator-(const proxy_random_access_iterator_wrapper<T>& in) const
>>> > +    {
>>> > +      ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo);
>>> > +      return this->ptr - in.ptr;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    subscript_proxy<T>
>>> > +    operator[](std::ptrdiff_t n) const
>>> > +    {
>>> > +      subscript_proxy<T> tmp = { *this + n };
>>> > +      return tmp;
>>> > +    }
>>> > +
>>> > +#if __cplusplus >= 201103L
>>> > +    // Ensure that the iterator's difference_type is always used.
>>> > +    template<typename D> void operator+=(D) = delete;
>>> > +    template<typename D> void operator-=(D) = delete;
>>> > +    template<typename D> void operator[](D) const = delete;
>>> > +    template<typename D>
>>> > +      typename std::enable_if<std::is_integral<D>::value>::type
>>> > +      operator-(D) const = delete;
>>> > +#endif
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    bool operator<(const proxy_random_access_iterator_wrapper<T>& in) 
>>> > const
>>> > +    {
>>> > +      ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo);
>>> > +      return this->ptr < in.ptr;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    bool operator>(const proxy_random_access_iterator_wrapper<T>& in) 
>>> > const
>>> > +    {
>>> > +      return in < *this;
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    bool operator>=(const proxy_random_access_iterator_wrapper<T>& in) 
>>> > const
>>> > +    {
>>> > +      return !(*this < in);
>>> > +    }
>>> > +
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    bool operator<=(const proxy_random_access_iterator_wrapper<T>& in) 
>>> > const
>>> > +    {
>>> > +      return !(*this > in);
>>> > +    }
>>> > +  };
>>> > +
>>> > +  template<typename T>
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper<T>
>>> > +    operator+(proxy_random_access_iterator_wrapper<T> it, std::ptrdiff_t 
>>> > n)
>>> > +    { return it += n; }
>>> > +
>>> > +  template<typename T>
>>> > +    _GLIBCXX14_CONSTEXPR
>>> > +    proxy_random_access_iterator_wrapper<T>
>>> > +    operator+(std::ptrdiff_t n, proxy_random_access_iterator_wrapper<T> 
>>> > it)
>>> > +    { return it += n; }
>>> > +
>>> > +#if __cplusplus >= 201103L
>>> > +    // Ensure that the iterator's difference_type is always used.
>>> > +    template<typename T, typename D>
>>> > +      void operator+(proxy_random_access_iterator_wrapper<T>, D) = 
>>> > delete;
>>> > +    template<typename T, typename D>
>>> > +      void operator+(D, proxy_random_access_iterator_wrapper<T>) = 
>>> > delete;
>>> > +#endif
>>> > +
>>> >    /**
>>> >     * @brief A container-type class for holding iterator wrappers
>>> >     * test_container takes two parameters, a class T and an iterator
>>> > --
>>> > 2.52.0
>>> >
>>>

Reply via email to