https://gcc.gnu.org/g:07ee6874963d2f8a787ba48341a5392ee8b6ba56
commit r14-10819-g07ee6874963d2f8a787ba48341a5392ee8b6ba56 Author: Patrick Palka <ppa...@redhat.com> Date: Fri Oct 4 10:01:39 2024 -0400 libstdc++/ranges: Implement various small LWG issues This implements the following small LWG issues: 3848. adjacent_view, adjacent_transform_view and slide_view missing base accessor 3851. chunk_view::inner-iterator missing custom iter_move and iter_swap 3947. Unexpected constraints on adjacent_transform_view::base() 4001. iota_view should provide empty 4012. common_view::begin/end are missing the simple-view check 4013. lazy_split_view::outer-iterator::value_type should not provide default constructor 4035. single_view should provide empty 4053. Unary call to std::views::repeat does not decay the argument 4054. Repeating a repeat_view should repeat the view libstdc++-v3/ChangeLog: * include/std/ranges (single_view::empty): Define as per LWG 4035. (iota_view::empty): Define as per LWG 4001. (lazy_split_view::_OuterIter::value_type): Remove default constructor and make other constructor private as per LWG 4013. (common_view::begin): Disable non-const overload for simple views as per LWG 4012. (common_view::end): Likewise. (adjacent_view::base): Define as per LWG 3848. (adjacent_transform_view::base): Likewise. (chunk_view::_InnerIter::iter_move): Define as per LWG 3851. (chunk_view::_InnerIter::itep_swap): Likewise. (slide_view::base): Define as per LWG 3848. (repeat_view): Adjust deduction guide as per LWG 4053. (_Repeat::operator()): Adjust single-parameter overload as per LWG 4054. * testsuite/std/ranges/adaptors/adjacent/1.cc: Verify existence of base member function. * testsuite/std/ranges/adaptors/adjacent_transform/1.cc: Likewise. * testsuite/std/ranges/adaptors/chunk/1.cc: Test LWG 3851 example. * testsuite/std/ranges/adaptors/slide/1.cc: Verify existence of base member function. * testsuite/std/ranges/iota/iota_view.cc: Test LWG 4001 example. * testsuite/std/ranges/repeat/1.cc: Test LWG 4053/4054 examples. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> (cherry picked from commit 20165d0107abd0f839f2519818b904f029f4ae55) Diff: --- libstdc++-v3/include/std/ranges | 84 +++++++++++++++++++--- .../testsuite/std/ranges/adaptors/adjacent/1.cc | 3 + .../std/ranges/adaptors/adjacent_transform/1.cc | 3 + .../testsuite/std/ranges/adaptors/chunk/1.cc | 15 ++++ .../testsuite/std/ranges/adaptors/slide/1.cc | 3 + .../testsuite/std/ranges/iota/iota_view.cc | 12 ++++ libstdc++-v3/testsuite/std/ranges/repeat/1.cc | 23 ++++++ 7 files changed, 135 insertions(+), 8 deletions(-) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 2c8a8535d396..c94463c83e53 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -331,6 +331,12 @@ namespace ranges end() const noexcept { return data() + 1; } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4035. single_view should provide empty + static constexpr bool + empty() noexcept + { return false; } + static constexpr size_t size() noexcept { return 1; } @@ -691,6 +697,12 @@ namespace ranges end() const requires same_as<_Winc, _Bound> { return _Iterator{_M_bound}; } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4001. iota_view should provide empty + constexpr bool + empty() const + { return _M_value == _M_bound; } + constexpr auto size() const requires (same_as<_Winc, _Bound> && __detail::__advanceable<_Winc>) @@ -3350,14 +3362,17 @@ namespace views::__adaptor private: _OuterIter _M_i = _OuterIter(); - public: - value_type() = default; - + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4013. lazy_split_view::outer-iterator::value_type should not + // provide default constructor constexpr explicit value_type(_OuterIter __i) : _M_i(std::move(__i)) { } + friend _OuterIter; + + public: constexpr _InnerIter<_Const> begin() const { return _InnerIter<_Const>{_M_i}; } @@ -3949,8 +3964,10 @@ namespace views::__adaptor base() && { return std::move(_M_base); } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4012. common_view::begin/end are missing the simple-view check constexpr auto - begin() + begin() requires (!__detail::__simple_view<_Vp>) { if constexpr (random_access_range<_Vp> && sized_range<_Vp>) return ranges::begin(_M_base); @@ -3970,7 +3987,7 @@ namespace views::__adaptor } constexpr auto - end() + end() requires (!__detail::__simple_view<_Vp>) { if constexpr (random_access_range<_Vp> && sized_range<_Vp>) return ranges::begin(_M_base) + ranges::size(_M_base); @@ -5317,6 +5334,16 @@ namespace views::__adaptor : _M_base(std::move(__base)) { } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3848. adjacent_view, adjacent_transform_view and slide_view missing base accessor + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + constexpr auto begin() requires (!__detail::__simple_view<_Vp>) { return _Iterator<false>(ranges::begin(_M_base), ranges::end(_M_base)); } @@ -5710,6 +5737,17 @@ namespace views::__adaptor : _M_fun(std::move(__fun)), _M_inner(std::move(__base)) { } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3848. adjacent_view, adjacent_transform_view and slide_view missing base accessor + // 3947. Unexpected constraints on adjacent_transform_view::base() + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_inner.base(); } + + constexpr _Vp + base() && + { return std::move(_M_inner.base()); } + constexpr auto begin() { return _Iterator<false>(*this, _M_inner.begin()); } @@ -6237,6 +6275,20 @@ namespace views::__adaptor operator-(const _InnerIter& __x, default_sentinel_t __y) requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> { return -(__y - __x); } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3851. chunk_view::inner-iterator missing custom iter_move and iter_swap + friend constexpr range_rvalue_reference_t<_Vp> + iter_move(const _InnerIter& __i) + noexcept(noexcept(ranges::iter_move(*__i._M_parent->_M_current))) + { return ranges::iter_move(*__i._M_parent->_M_current); } + + friend constexpr void + iter_swap(const _InnerIter& __x, const _InnerIter& __y) + noexcept(noexcept(ranges::iter_swap(*__x._M_parent->_M_current, + *__x._M_parent->_M_current))) + requires indirectly_swappable<iterator_t<_Vp>> + { return ranges::iter_swap(*__x._M_parent->_M_current, *__y._M_parent->_M_current); } }; template<view _Vp> @@ -6578,6 +6630,16 @@ namespace views::__adaptor : _M_base(std::move(__base)), _M_n(__n) { __glibcxx_assert(__n > 0); } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3848. adjacent_view, adjacent_transform_view and slide_view missing base accessor + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + constexpr auto begin() requires (!(__detail::__simple_view<_Vp> && __detail::__slide_caches_nothing<const _Vp>)) @@ -7693,8 +7755,10 @@ namespace views::__adaptor { return __detail::__to_unsigned_like(_M_bound); } }; - template<typename _Tp, typename _Bound> - repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4053. Unary call to std::views::repeat does not decay the argument + template<typename _Tp, typename _Bound = unreachable_sentinel_t> + repeat_view(_Tp, _Bound = _Bound()) -> repeat_view<_Tp, _Bound>; template<move_constructible _Tp, semiregular _Bound> requires is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> @@ -7841,7 +7905,11 @@ namespace views::__adaptor requires __detail::__can_repeat_view<_Tp> constexpr auto operator() [[nodiscard]] (_Tp&& __value) const - { return repeat_view(std::forward<_Tp>(__value)); } + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4054. Repeating a repeat_view should repeat the view + return repeat_view<decay_t<_Tp>>(std::forward<_Tp>(__value)); + } template<typename _Tp, typename _Bound> requires __detail::__can_bounded_repeat_view<_Tp, _Bound> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc index b83743a745c2..085cd4a8c54c 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc @@ -47,6 +47,9 @@ test01() VERIFY( &std::get<2>(v3[i]) == &y[i] + 2 ); } + // LWG 3848 - adjacent_view etc missing base accessor + v3.base(); + const auto v5 = y | views::adjacent<5>; VERIFY( ranges::equal(v5, views::single(std::make_tuple(1, 2, 3, 4, 5))) ); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc index 6aeedbaa6485..a5791b3da702 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc @@ -39,6 +39,9 @@ test01() VERIFY( ranges::size(v3) == 4 ); VERIFY( ranges::equal(v3, (int[]){3, 4, 5, 6}) ); + // LWG 3848 - adjacent_transform_view etc missing base accessor + v3.base(); + const auto v6 = y | views::adjacent_transform<6>([](auto...) { return 0; }); VERIFY( ranges::equal(v6, (int[]){0}) ); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc index 90eb608ca5eb..82f9f1b674c9 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc @@ -8,6 +8,7 @@ #endif #include <algorithm> +#include <sstream> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -76,10 +77,24 @@ test02() VERIFY( ranges::equal(wrapper(x) | views::chunk(i) | views::join, x) ); } +void +test03() +{ + // LWG 3851 - chunk_view::inner-iterator missing custom iter_move and iter_swap + auto ints = std::istringstream{"0 1 2 3 4"}; + std::vector<std::string> vs{"the", "quick", "brown", "fox"}; + auto r = views::zip(vs, views::istream<int>(ints)) | views::chunk(2) | views::join; + std::vector<std::tuple<std::string, int>> res; + ranges::copy(std::move_iterator(r.begin()), std::move_sentinel(r.end()), + std::back_inserter(res)); + VERIFY( vs.front().empty() ); +} + int main() { static_assert(test01()); test02<__gnu_test::test_input_range<int>>(); test02<__gnu_test::test_forward_range<int>>(); + test03(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/slide/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/slide/1.cc index bafe9fbc4bf7..a5d94ea64062 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/slide/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/slide/1.cc @@ -50,6 +50,9 @@ test01() VERIFY( &v3[i][2] == &y[i] + 2 ); } + // LWG 3848 - slide_view etc missing base accessor + v3.base(); + const auto v5 = y | views::slide(5); VERIFY( ranges::size(v5) == 1 ); VERIFY( ranges::equal(v5 | views::join, y) ); diff --git a/libstdc++-v3/testsuite/std/ranges/iota/iota_view.cc b/libstdc++-v3/testsuite/std/ranges/iota/iota_view.cc index bec4174a3dad..9db61a765966 100644 --- a/libstdc++-v3/testsuite/std/ranges/iota/iota_view.cc +++ b/libstdc++-v3/testsuite/std/ranges/iota/iota_view.cc @@ -19,6 +19,7 @@ #include <algorithm> #include <ranges> +#include <vector> #include <testsuite_hooks.h> void @@ -118,6 +119,16 @@ test07() static_assert(!requires { iota(nullptr, nullptr); }); } +void +test08() +{ + // LWC 4001 - iota_view should provide empty + std::vector<int> v; + auto it = std::back_inserter(v); + auto r = std::views::iota(it); + VERIFY( !r.empty() ); +} + int main() { @@ -128,4 +139,5 @@ main() test05(); test06(); test07(); + test08(); } diff --git a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc index cad8c8dcbe72..eb0c848e9ce3 100644 --- a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc @@ -151,6 +151,27 @@ test07() auto d2 = std::views::repeat(std::make_unique<int>(5), 4) | std::views::drop(2); } +void +test08() +{ + // LWG 4053 - Unary call to std::views::repeat does not decay the argument + using type = ranges::repeat_view<const char*>; + using type = decltype(views::repeat("foo", std::unreachable_sentinel)); + using type = decltype(views::repeat(+"foo", std::unreachable_sentinel)); + using type = decltype(views::repeat("foo")); + using type = decltype(views::repeat(+"foo")); +} + +void +test09() +{ + // LWG 4054 - Repeating a repeat_view should repeat the view + auto v = views::repeat(views::repeat(5)); + using type = decltype(v); + using type = ranges::repeat_view<ranges::repeat_view<int>>; + VERIFY( v[0][0] == 5 ); +} + int main() { @@ -161,4 +182,6 @@ main() test05(); test06(); test07(); + test08(); + test09(); }