On Wed, 10 Jun 2020, Jonathan Wakely wrote:

> On 10/06/20 15:32 -0400, Patrick Palka via Libstdc++ wrote:
> > ranges::copy and a number of other ranges algorithms have unwrapping
> > optimizations for iterators of type __normal_iterator, move_iterator and
> > reverse_iterator.  But in the checks that guard these optimizations we
> > currently only test that the iterator of the iterator/sentinel pair has
> > the appropriate type before proceeding with the corresponding
> > optimization, and we fail to also test the sentinel type.
> > 
> > This breaks the testcase in this PR because this testcase constructs
> > via range adaptors a range whose begin() is a __normal_iterator and
> > whose end() is a custom sentinel type, and then does ranges::copy on it.
> > From there we bogusly perform the __normal_iterator unwrapping
> > optimization on this iterator/sentinel pair, which immediately leads to
> > a constraint failure since the custom sentinel type does not model
> > sentinel_for<int*>.
> > 
> > This patch fixes this issue by augmenting each of the problematic checks
> > to also test that the iterator and sentinel types are the same before
> > applying the corresponding unwrapping optimization.
> > 
> > Tested on x86_64-pc-linux-gnu, does this look OK to commit to master to
> > and to the 10.2 branch?
> > 
> > libstdc++-v3/ChangeLog:
> > 
> >     PR libstdc++/95578
> >     * include/bits/ranges_algo.h (__lexicographical_compare_fn):
> >     Also check that the iterator and sentinel have the same type before
> >     applying the unwrapping optimization for __normal_iterator.
> >     Split the check into two, one for the first iterator/sentinel
> >     pair and another for second iterator/sentinel pair.  Stop using
> >     __niter_base and stop doing std::move on a __normal_iterator.
> >     * include/bits/ranges_algobase.c (__equal_fn): Likewise.
> >     (__copy_or_move): Likewise.  Perform similar adjustments for
> >     the reverse_iterator and move_iterator optimizations.  Inline
> >     the checks into the if-constexprs, and use using-declarations to
> >     make them less visually noisy.  Don't use __niter_wrap.
> >     (__copy_or_move_backward): Likewise.
> >     * testsuite/25_algorithms/copy/95578.cc: New test.
> >     * testsuite/25_algorithms/copy_backward/95578.cc: New test.
> >     * testsuite/25_algorithms/equal/95578.cc: New test.
> >     * testsuite/25_algorithms/lexicographical_compare/95578.cc: New test.
> >     * testsuite/25_algorithms/move/95578.cc: New test.
> >     * testsuite/25_algorithms/move_backward/95578.cc: New test.
> > ---
> > libstdc++-v3/include/bits/ranges_algo.h       | 14 +--
> > libstdc++-v3/include/bits/ranges_algobase.h   | 88 ++++++++++---------
> > .../testsuite/25_algorithms/copy/95578.cc     | 74 ++++++++++++++++
> > .../25_algorithms/copy_backward/95578.cc      | 62 +++++++++++++
> > .../testsuite/25_algorithms/equal/95578.cc    | 74 ++++++++++++++++
> > .../lexicographical_compare/95578.cc          | 74 ++++++++++++++++
> > .../testsuite/25_algorithms/move/95578.cc     | 62 +++++++++++++
> > .../25_algorithms/move_backward/95578.cc      | 62 +++++++++++++
> > 8 files changed, 465 insertions(+), 45 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy/95578.cc
> > create mode 100644
> > libstdc++-v3/testsuite/25_algorithms/copy_backward/95578.cc
> > create mode 100644 libstdc++-v3/testsuite/25_algorithms/equal/95578.cc
> > create mode 100644
> > libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/95578.cc
> > create mode 100644 libstdc++-v3/testsuite/25_algorithms/move/95578.cc
> > create mode 100644
> > libstdc++-v3/testsuite/25_algorithms/move_backward/95578.cc
> > 
> > diff --git a/libstdc++-v3/include/bits/ranges_algo.h
> > b/libstdc++-v3/include/bits/ranges_algo.h
> > index c038a505afa..94ca7b6488d 100644
> > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > @@ -3452,11 +3452,15 @@ namespace ranges
> >              _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
> >       {
> >     if constexpr (__detail::__is_normal_iterator<_Iter1>
> > -                 || __detail::__is_normal_iterator<_Iter2>)
> > -     return (*this)(std::__niter_base(std::move(__first1)),
> > -                    std::__niter_base(std::move(__last1)),
> > -                    std::__niter_base(std::move(__first2)),
> > -                    std::__niter_base(std::move(__last2)),
> > +                 && same_as<_Iter1, _Sent1>)
> > +     return (*this)(__first1.base(), __last1.base(),
> > +                    std::move(__first2), std::move(__last2),
> > +                    std::move(__comp),
> > +                    std::move(__proj1), std::move(__proj2));
> > +   else if constexpr (__detail::__is_normal_iterator<_Iter2>
> > +                      && same_as<_Iter2, _Sent2>)
> > +     return (*this)(std::move(__first1), std::move(__last1),
> > +                    __first2.base(), __last2.base(),
> >                      std::move(__comp),
> >                      std::move(__proj1), std::move(__proj2));
> >     else
> 
> So if all four iterators are normal_iterator then we first unwrap the
> first pair, and recurse, and then unwrap the second pair, and recurse,
> right?
> 
> I don't love that additional step needed to unwrap the scond time, but
> I can live with it.

Hmm, yes..  I guess we could circumvent this second step by adding an
additional constexpr-if branch.  Better might be to introduce new
helpers that are like __niter_base and __niter_wrap but instead take
iterator/sentinel pairs.  I wonder if there are other options?

> 
> OK for master and gcc-10, thanks.

Thanks.

Reply via email to