Re: [PATCH] libstdc++: Make equal and is_permutation short-circuit (LWG 3560)
On Thu, 14 Nov 2024 at 17:13, Jonathan Wakely wrote: > > We already implement short-circuiting for random access iterators, but > we also need to do so for ranges::equal and ranges::is_permutation when > given sized ranges that are not random access ranges (e.g. std::list). > > libstdc++-v3/ChangeLog: > > * include/bits/ranges_algo.h (__is_permutation_fn::operator()): > Short-circuit for sized ranges with different sizes, as per LWG > 3560. > * include/bits/ranges_algobase.h (__equal_fn::operator()): > Likewise. > * include/bits/stl_algo.h (__is_permutation): Use if-constexpr > for random access iterator branches. > * include/bits/stl_algobase.h (__equal4): Likewise. > * testsuite/25_algorithms/equal/lwg3560.cc: New test. > * testsuite/25_algorithms/is_permutation/lwg3560.cc: New test. > --- > > Tested x86_64-linux. > > Also available for review at: > https://forge.sourceware.org/gcc/gcc-TEST/pulls/24 > > libstdc++-v3/include/bits/ranges_algo.h | 7 +++ > libstdc++-v3/include/bits/ranges_algobase.h | 8 +++ > libstdc++-v3/include/bits/stl_algo.h | 13 ++--- > libstdc++-v3/include/bits/stl_algobase.h | 44 > .../testsuite/25_algorithms/equal/lwg3560.cc | 49 ++ > .../25_algorithms/is_permutation/lwg3560.cc | 51 +++ > 6 files changed, 146 insertions(+), 26 deletions(-) > create mode 100644 libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc > create mode 100644 > libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h > b/libstdc++-v3/include/bits/ranges_algo.h > index bae36637b3e..80d4f5a0d57 100644 > --- a/libstdc++-v3/include/bits/ranges_algo.h > +++ b/libstdc++-v3/include/bits/ranges_algo.h > @@ -595,6 +595,13 @@ namespace ranges >operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, > _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >{ > + // _GLIBCXX_RESOLVE_LIB_DEFECTS > + // 3560. ranges::is_permutation should short-circuit for sized_ranges > + if constexpr (sized_range<_Range1>) > + if constexpr (sized_range<_Range2>) > + if (ranges::distance(__r1) != ranges::distance(__r2)) > + return false; > + > return (*this)(ranges::begin(__r1), ranges::end(__r1), >ranges::begin(__r2), ranges::end(__r2), >std::move(__pred), > diff --git a/libstdc++-v3/include/bits/ranges_algobase.h > b/libstdc++-v3/include/bits/ranges_algobase.h > index df4e770e7a6..3321085c4f6 100644 > --- a/libstdc++-v3/include/bits/ranges_algobase.h > +++ b/libstdc++-v3/include/bits/ranges_algobase.h > @@ -172,6 +172,14 @@ namespace ranges >operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, > _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const >{ > + // _GLIBCXX_RESOLVE_LIB_DEFECTS > + // 3560. ranges::equal [...] should short-circuit for sized_ranges > + if constexpr (sized_range<_Range1>) > + if constexpr (sized_range<_Range2>) > + if (ranges::distance(__r1) != ranges::distance(__r2)) > + return false; > + > + if constexpr (sized_range<_Range1>) I'm not sure how this extra line got in the version I posted. I've removed that locally, and re-pushed to the forge. > return (*this)(ranges::begin(__r1), ranges::end(__r1), >ranges::begin(__r2), ranges::end(__r2), >std::move(__pred), > diff --git a/libstdc++-v3/include/bits/stl_algo.h > b/libstdc++-v3/include/bits/stl_algo.h > index 04bdaa66981..d8a7668ff83 100644 > --- a/libstdc++-v3/include/bits/stl_algo.h > +++ b/libstdc++-v3/include/bits/stl_algo.h > @@ -3471,6 +3471,8 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) > } > > #if __cplusplus > 201103L > +#pragma GCC diagnostic push > +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr >templatetypename _BinaryPredicate> > _GLIBCXX20_CONSTEXPR > @@ -3485,12 +3487,10 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) > = typename iterator_traits<_ForwardIterator2>::iterator_category; >using _It1_is_RA = is_same<_Cat1, random_access_iterator_tag>; >using _It2_is_RA = is_same<_Cat2, random_access_iterator_tag>; > - constexpr bool __ra_iters = _It1_is_RA() && _It2_is_RA(); > - if (__ra_iters) > + constexpr bool __ra_iters = __and_<_It1_is_RA, _It2_is_RA>::value; > + if constexpr (__ra_iters) > { > - auto __d1 = std::distance(__first1, __last1); > - auto __d2 = std::distance(__first2, __last2); > - if (__d1 != __d2) > + if ((__last1 - __first1) != (__last2 - __first2)) > return false; > } > > @@ -3501,7 +3501,7 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) > if (!__pred(__first1, __first2)) >
[PATCH] libstdc++: Make equal and is_permutation short-circuit (LWG 3560)
We already implement short-circuiting for random access iterators, but we also need to do so for ranges::equal and ranges::is_permutation when given sized ranges that are not random access ranges (e.g. std::list). libstdc++-v3/ChangeLog: * include/bits/ranges_algo.h (__is_permutation_fn::operator()): Short-circuit for sized ranges with different sizes, as per LWG 3560. * include/bits/ranges_algobase.h (__equal_fn::operator()): Likewise. * include/bits/stl_algo.h (__is_permutation): Use if-constexpr for random access iterator branches. * include/bits/stl_algobase.h (__equal4): Likewise. * testsuite/25_algorithms/equal/lwg3560.cc: New test. * testsuite/25_algorithms/is_permutation/lwg3560.cc: New test. --- Tested x86_64-linux. Also available for review at: https://forge.sourceware.org/gcc/gcc-TEST/pulls/24 libstdc++-v3/include/bits/ranges_algo.h | 7 +++ libstdc++-v3/include/bits/ranges_algobase.h | 8 +++ libstdc++-v3/include/bits/stl_algo.h | 13 ++--- libstdc++-v3/include/bits/stl_algobase.h | 44 .../testsuite/25_algorithms/equal/lwg3560.cc | 49 ++ .../25_algorithms/is_permutation/lwg3560.cc | 51 +++ 6 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc create mode 100644 libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index bae36637b3e..80d4f5a0d57 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -595,6 +595,13 @@ namespace ranges operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3560. ranges::is_permutation should short-circuit for sized_ranges + if constexpr (sized_range<_Range1>) + if constexpr (sized_range<_Range2>) + if (ranges::distance(__r1) != ranges::distance(__r2)) + return false; + return (*this)(ranges::begin(__r1), ranges::end(__r1), ranges::begin(__r2), ranges::end(__r2), std::move(__pred), diff --git a/libstdc++-v3/include/bits/ranges_algobase.h b/libstdc++-v3/include/bits/ranges_algobase.h index df4e770e7a6..3321085c4f6 100644 --- a/libstdc++-v3/include/bits/ranges_algobase.h +++ b/libstdc++-v3/include/bits/ranges_algobase.h @@ -172,6 +172,14 @@ namespace ranges operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3560. ranges::equal [...] should short-circuit for sized_ranges + if constexpr (sized_range<_Range1>) + if constexpr (sized_range<_Range2>) + if (ranges::distance(__r1) != ranges::distance(__r2)) + return false; + + if constexpr (sized_range<_Range1>) return (*this)(ranges::begin(__r1), ranges::end(__r1), ranges::begin(__r2), ranges::end(__r2), std::move(__pred), diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h index 04bdaa66981..d8a7668ff83 100644 --- a/libstdc++-v3/include/bits/stl_algo.h +++ b/libstdc++-v3/include/bits/stl_algo.h @@ -3471,6 +3471,8 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) } #if __cplusplus > 201103L +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr template _GLIBCXX20_CONSTEXPR @@ -3485,12 +3487,10 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) = typename iterator_traits<_ForwardIterator2>::iterator_category; using _It1_is_RA = is_same<_Cat1, random_access_iterator_tag>; using _It2_is_RA = is_same<_Cat2, random_access_iterator_tag>; - constexpr bool __ra_iters = _It1_is_RA() && _It2_is_RA(); - if (__ra_iters) + constexpr bool __ra_iters = __and_<_It1_is_RA, _It2_is_RA>::value; + if constexpr (__ra_iters) { - auto __d1 = std::distance(__first1, __last1); - auto __d2 = std::distance(__first2, __last2); - if (__d1 != __d2) + if ((__last1 - __first1) != (__last2 - __first2)) return false; } @@ -3501,7 +3501,7 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) if (!__pred(__first1, __first2)) break; - if (__ra_iters) + if constexpr (__ra_iters) { if (__first1 == __last1) return true; @@ -3532,6 +3532,7 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) } return true; } +#pragma GCC diagnostic pop /** * @brief Checks whether a permutaion of the second sequence is equal diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc+