diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-11-14 16:57:17 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-11-14 20:01:25 +0000 |
commit | 45cc42d6dc0642612e7076e95820438a1aab5479 (patch) | |
tree | 3b0dbe694c500a81a359cbda541e3a9d602828a5 | |
parent | 012f5a22bac26a898ab66655965b07ac23201fdd (diff) | |
download | gcc-45cc42d6dc0642612e7076e95820438a1aab5479.zip gcc-45cc42d6dc0642612e7076e95820438a1aab5479.tar.gz gcc-45cc42d6dc0642612e7076e95820438a1aab5479.tar.bz2 |
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.
Reviewed-by: Patrick Palka <ppalka@redhat.com>
-rw-r--r-- | libstdc++-v3/include/bits/ranges_algo.h | 7 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/ranges_algobase.h | 7 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/stl_algo.h | 13 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/stl_algobase.h | 44 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc | 49 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc | 51 |
6 files changed, 145 insertions, 26 deletions
diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index bae3663..80d4f5a 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 df4e770..150e990 100644 --- a/libstdc++-v3/include/bits/ranges_algobase.h +++ b/libstdc++-v3/include/bits/ranges_algobase.h @@ -172,6 +172,13 @@ 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; + 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 04bdaa6..d8a7668 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<typename _ForwardIterator1, typename _ForwardIterator2, typename _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)) 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++-v3/include/bits/stl_algobase.h index 9ecd0b2..d6f55dc 100644 --- a/libstdc++-v3/include/bits/stl_algobase.h +++ b/libstdc++-v3/include/bits/stl_algobase.h @@ -1640,6 +1640,9 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO } #if __cplusplus >= 201103L +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + // 4-iterator version of std::equal<It1, It2> for use in C++11. template<typename _II1, typename _II2> _GLIBCXX20_CONSTEXPR @@ -1650,20 +1653,20 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO using _Cat1 = typename iterator_traits<_II1>::iterator_category; using _Cat2 = typename iterator_traits<_II2>::iterator_category; using _RAIters = __and_<is_same<_Cat1, _RATag>, is_same<_Cat2, _RATag>>; - if (_RAIters()) + if constexpr (_RAIters::value) { - auto __d1 = std::distance(__first1, __last1); - auto __d2 = std::distance(__first2, __last2); - if (__d1 != __d2) + if ((__last1 - __first1) != (__last2 - __first2)) return false; return _GLIBCXX_STD_A::equal(__first1, __last1, __first2); } - - for (; __first1 != __last1 && __first2 != __last2; - ++__first1, (void)++__first2) - if (!(*__first1 == *__first2)) - return false; - return __first1 == __last1 && __first2 == __last2; + else + { + for (; __first1 != __last1 && __first2 != __last2; + ++__first1, (void)++__first2) + if (!(*__first1 == *__first2)) + return false; + return __first1 == __last1 && __first2 == __last2; + } } // 4-iterator version of std::equal<It1, It2, BinaryPred> for use in C++11. @@ -1677,22 +1680,23 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO using _Cat1 = typename iterator_traits<_II1>::iterator_category; using _Cat2 = typename iterator_traits<_II2>::iterator_category; using _RAIters = __and_<is_same<_Cat1, _RATag>, is_same<_Cat2, _RATag>>; - if (_RAIters()) + if constexpr (_RAIters::value) { - auto __d1 = std::distance(__first1, __last1); - auto __d2 = std::distance(__first2, __last2); - if (__d1 != __d2) + if ((__last1 - __first1) != (__last2 - __first2)) return false; return _GLIBCXX_STD_A::equal(__first1, __last1, __first2, __binary_pred); } - - for (; __first1 != __last1 && __first2 != __last2; - ++__first1, (void)++__first2) - if (!bool(__binary_pred(*__first1, *__first2))) - return false; - return __first1 == __last1 && __first2 == __last2; + else + { + for (; __first1 != __last1 && __first2 != __last2; + ++__first1, (void)++__first2) + if (!bool(__binary_pred(*__first1, *__first2))) + return false; + return __first1 == __last1 && __first2 == __last2; + } } +#pragma GCC diagnostic pop #endif // C++11 #ifdef __glibcxx_robust_nonmodifying_seq_ops // C++ >= 14 diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc b/libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc new file mode 100644 index 0000000..7bf8486 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/equal/lwg3560.cc @@ -0,0 +1,49 @@ +// { dg-do run { target c++20 } } + +// LWG 3560. ranges::equal and ranges::is_permutation should short-circuit +// for sized_ranges + +#include <algorithm> +#include <testsuite_iterators.h> +#include <testsuite_hooks.h> + +struct X +{ + // Any equality comparison will cause the test to fail. + bool operator==(const X&) const { VERIFY(false); } +}; + +void +test_std_equal() +{ + X vals[3]; + __gnu_test::random_access_container<X> r1(vals, vals+3); + __gnu_test::random_access_container<X> r2(vals, vals+2); + VERIFY( ! std::equal(r1.begin(), r1.end(), r2.begin(), r2.end()) ); + + std::ranges::equal_to pred; + VERIFY( ! std::equal(r1.begin(), r1.end(), r2.begin(), r2.end(), pred) ); +} + +void +test_std_ranges_equal() +{ + X vals[3]; + __gnu_test::test_random_access_range<X> r1(vals, vals+3); + __gnu_test::test_random_access_range<X> r2(vals, vals+2); + + // Any application of the projection will cause the test to fail. + auto proj = [](const X&) -> const X& { VERIFY(false); }; + VERIFY( ! std::ranges::equal(r1.begin(), r1.end(), r2.begin(), r2.end(), {}, + proj, proj) ); + + __gnu_test::test_forward_sized_range<X> r3(vals, vals+3); + __gnu_test::test_forward_sized_range<X> r4(vals, vals+2); + VERIFY( ! std::ranges::equal(r3, r4, {}, proj, proj) ); +} + +int main() +{ + test_std_equal(); + test_std_ranges_equal(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc b/libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc new file mode 100644 index 0000000..f9469b0 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/is_permutation/lwg3560.cc @@ -0,0 +1,51 @@ +// { dg-do run { target c++20 } } + +// LWG 3560. ranges::equal and ranges::is_permutation should short-circuit +// for sized_ranges + +#include <algorithm> +#include <testsuite_iterators.h> +#include <testsuite_hooks.h> + +struct X +{ + // Any equality comparison will cause the test to fail. + bool operator==(const X&) const { VERIFY(false); } +}; + +void +test_std_is_permutation() +{ + X vals[3]; + __gnu_test::random_access_container<X> r1(vals, vals+3); + __gnu_test::random_access_container<X> r2(vals, vals+2); + VERIFY( ! std::is_permutation(r1.begin(), r1.end(), r2.begin(), r2.end()) ); + + std::ranges::equal_to pred; + VERIFY( ! std::is_permutation(r1.begin(), r1.end(), r2.begin(), r2.end(), + pred) ); +} + +void +test_std_ranges_is_permutation() +{ + X vals[3]; + __gnu_test::test_random_access_range<X> r1(vals, vals+3); + __gnu_test::test_random_access_range<X> r2(vals, vals+2); + + // Any application of the projection will cause the test to fail. + auto proj = [](const X&) -> const X& { VERIFY(false); }; + VERIFY( ! std::ranges::is_permutation(r1.begin(), r1.end(), + r2.begin(), r2.end(), + {}, proj, proj) ); + + __gnu_test::test_forward_sized_range<X> r3(vals, vals+3); + __gnu_test::test_forward_sized_range<X> r4(vals, vals+2); + VERIFY( ! std::ranges::is_permutation(r3, r4, {}, proj, proj) ); +} + +int main() +{ + test_std_is_permutation(); + test_std_ranges_is_permutation(); +} |