From 5e1b17f038671df1a6580ece4cd58ef91cc1e234 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Tue, 22 Feb 2022 09:37:58 -0500 Subject: [PATCH] libstdc++: Implement P2415R2 changes to viewable_range / views::all This implements the wording changes in P2415R2 "What is a view?", which is a DR for C++20. libstdc++-v3/ChangeLog: * include/bits/ranges_base.h (__detail::__is_initializer_list): Define. (viewable_range): Adjust as per P2415R2. * include/bits/ranges_cmp.h (__cpp_lib_ranges): Adjust value. * include/std/ranges (owning_view): Define as per P2415R2. (enable_borrowed_range): Likewise. (views::__detail::__can_subrange): Replace with ... (views::__detail::__can_owning_view): ... this. (views::_All::_S_noexcept): Sync with operator(). (views::_All::operator()): Use owning_view instead of subrange as per P2415R2. * include/std/version (__cpp_lib_ranges): Adjust value. * testsuite/std/ranges/adaptors/all.cc (test06): Adjust now that views::all uses owning_view instead of subrange. (test08): New test. * testsuite/std/ranges/adaptors/lazy_split.cc (test09): Adjust now that rvalue non-view non-borrowed ranges are viewable. * testsuite/std/ranges/adaptors/split.cc (test06): Likewise. --- libstdc++-v3/include/bits/ranges_base.h | 16 +++- libstdc++-v3/include/bits/ranges_cmp.h | 2 +- libstdc++-v3/include/std/ranges | 89 ++++++++++++++++++- libstdc++-v3/include/std/version | 2 +- .../testsuite/std/ranges/adaptors/all.cc | 59 ++++++++---- .../std/ranges/adaptors/lazy_split.cc | 13 ++- .../testsuite/std/ranges/adaptors/split.cc | 13 ++- 7 files changed, 159 insertions(+), 35 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 3c5f4b1790a..38db33fd2ce 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -634,7 +634,7 @@ namespace ranges template concept __is_derived_from_view_interface = requires (_Tp __t) { __is_derived_from_view_interface_fn(__t, __t); }; - } + } // namespace __detail /// [range.view] The ranges::view_base type. struct view_base { }; @@ -689,11 +689,23 @@ namespace ranges concept common_range = range<_Tp> && same_as, sentinel_t<_Tp>>; + namespace __detail + { + template + inline constexpr bool __is_initializer_list = false; + + template + inline constexpr bool __is_initializer_list> = true; + } // namespace __detail + /// A range which can be safely converted to a view. template concept viewable_range = range<_Tp> && ((view> && constructible_from, _Tp>) - || (!view> && borrowed_range<_Tp>)); + || (!view> + && (is_lvalue_reference_v<_Tp> + || (movable> + && !__detail::__is_initializer_list>)))); // [range.iter.ops] range iterator operations diff --git a/libstdc++-v3/include/bits/ranges_cmp.h b/libstdc++-v3/include/bits/ranges_cmp.h index c84fb2ac0a2..6a5ad3de6e7 100644 --- a/libstdc++-v3/include/bits/ranges_cmp.h +++ b/libstdc++-v3/include/bits/ranges_cmp.h @@ -57,7 +57,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_concepts // Define this here, included by all the headers that need to define it. -#define __cpp_lib_ranges 202106L +#define __cpp_lib_ranges 202110L namespace ranges { diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index ac85907f129..3e71ecb32b7 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -1144,6 +1144,87 @@ namespace views::__adaptor template inline constexpr bool enable_borrowed_range> = true; + template + requires movable<_Range> + && (!__detail::__is_initializer_list>) + class owning_view : public view_interface> + { + private: + _Range _M_r = _Range(); + + public: + owning_view() requires default_initializable<_Range> = default; + + constexpr + owning_view(_Range&& __t) + noexcept(is_nothrow_move_constructible_v<_Range>) + : _M_r(std::move(__t)) + { } + + owning_view(owning_view&&) = default; + owning_view& operator=(owning_view&&) = default; + + constexpr _Range& + base() & noexcept + { return _M_r; } + + constexpr const _Range& + base() const& noexcept + { return _M_r; } + + constexpr _Range&& + base() && noexcept + { return std::move(_M_r); } + + constexpr const _Range&& + base() const&& noexcept + { return std::move(_M_r); } + + constexpr iterator_t<_Range> + begin() + { return ranges::begin(_M_r); } + + constexpr sentinel_t<_Range> + end() + { return ranges::end(_M_r); } + + constexpr auto + begin() const requires range + { return ranges::begin(_M_r); } + + constexpr auto + end() const requires range + { return ranges::end(_M_r); } + + constexpr bool + empty() requires requires { ranges::empty(_M_r); } + { return ranges::empty(_M_r); } + + constexpr bool + empty() const requires requires { ranges::empty(_M_r); } + { return ranges::empty(_M_r); } + + constexpr auto + size() requires sized_range<_Range> + { return ranges::size(_M_r); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_r); } + + constexpr auto + data() requires contiguous_range<_Range> + { return ranges::data(_M_r); } + + constexpr auto + data() const requires contiguous_range + { return ranges::data(_M_r); } + }; + + template + inline constexpr bool enable_borrowed_range> + = enable_borrowed_range<_Tp>; + namespace views { namespace __detail @@ -1152,7 +1233,7 @@ namespace views::__adaptor concept __can_ref_view = requires { ref_view{std::declval<_Range>()}; }; template - concept __can_subrange = requires { subrange{std::declval<_Range>()}; }; + concept __can_owning_view = requires { owning_view{std::declval<_Range>()}; }; } // namespace __detail struct _All : __adaptor::_RangeAdaptorClosure @@ -1166,13 +1247,13 @@ namespace views::__adaptor else if constexpr (__detail::__can_ref_view<_Range>) return true; else - return noexcept(subrange{std::declval<_Range>()}); + return noexcept(owning_view{std::declval<_Range>()}); } template requires view> || __detail::__can_ref_view<_Range> - || __detail::__can_subrange<_Range> + || __detail::__can_owning_view<_Range> constexpr auto operator() [[nodiscard]] (_Range&& __r) const noexcept(_S_noexcept<_Range>()) @@ -1182,7 +1263,7 @@ namespace views::__adaptor else if constexpr (__detail::__can_ref_view<_Range>) return ref_view{std::forward<_Range>(__r)}; else - return subrange{std::forward<_Range>(__r)}; + return owning_view{std::forward<_Range>(__r)}; } static constexpr bool _S_has_simple_call_op = true; diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 24311ee05c8..461e65b5fab 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -273,7 +273,7 @@ #define __cpp_lib_optional 202106L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts -# define __cpp_lib_ranges 202106L +# define __cpp_lib_ranges 202110L #endif #if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE # define __cpp_lib_semaphore 201907L diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc index c25d972274f..e457462825d 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc @@ -19,7 +19,9 @@ // { dg-do run { target c++2a } } #include +#include #include +#include #include #include @@ -130,20 +132,6 @@ test05() static_assert(!requires { 0 | all; }); } -template -struct BorrowedRange -{ - int* ptr = nullptr; - - BorrowedRange(int (&arr)[3]) noexcept : ptr(arr) { } - - int* begin() const noexcept(B1) { return ptr; } - int* end() const noexcept(B2) { return ptr + 3; } -}; - -template -const bool std::ranges::enable_borrowed_range> = true; - void test06() { @@ -152,11 +140,11 @@ test06() // Using ref_view: static_assert(noexcept(views::all(x))); - // Using subrange: - static_assert(noexcept(views::all(BorrowedRange(x)))); - static_assert(!noexcept(views::all(BorrowedRange(x)))); - static_assert(!noexcept(views::all(BorrowedRange(x)))); - static_assert(!noexcept(views::all(BorrowedRange(x)))); + // Using owning_view: + static_assert(noexcept(views::all(std::array{}))); + struct A { A(); A(const A&); }; + static_assert(!std::is_nothrow_move_constructible_v>); + static_assert(!noexcept(views::all(std::array{}))); } void @@ -173,6 +161,38 @@ test07() static_assert(!ranges::viewable_range); } +constexpr bool +test08() +{ + // Verify P2415R2 "What is a view?" changes. + // In particular, rvalue non-view non-borrowed ranges are now viewable. + static_assert(ranges::viewable_range&&>); + static_assert(!ranges::viewable_range&&>); + + static_assert(ranges::viewable_range&>); + static_assert(ranges::viewable_range&>); + static_assert(!ranges::viewable_range&&>); + static_assert(!ranges::viewable_range&&>); + + using type = views::all_t&&>; + using type = ranges::owning_view>; + + std::same_as auto v = std::vector{{1,2,3}} | views::all; + + VERIFY( ranges::equal(v, (int[]){1,2,3}) ); + VERIFY( ranges::size(v) == 3 ); + VERIFY( !ranges::empty(v) ); + VERIFY( ranges::data(v) == &v[0] ); + + const auto w = std::move(v); + VERIFY( ranges::equal(w, (int[]){1,2,3}) ); + VERIFY( ranges::size(w) == 3 ); + VERIFY( !ranges::empty(w) ); + VERIFY( ranges::data(w) == &w[0] ); + + return true; +} + int main() { @@ -183,4 +203,5 @@ main() test05(); test06(); test07(); + static_assert(test08()); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc index a7d3dd6a23e..e46f76d7632 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc @@ -163,10 +163,15 @@ test09() static_assert(!requires { lazy_split(p)(); }); static_assert(!requires { s | lazy_split; }); - static_assert(!requires { s | lazy_split(p); }); - static_assert(!requires { lazy_split(p)(s); }); - static_assert(!requires { s | (lazy_split(p) | views::all); }); - static_assert(!requires { (lazy_split(p) | views::all)(s); }); + // Test the case where the closure object is used as an rvalue and therefore + // the copy of p is forwarded as an rvalue. + // This used to be invalid, but is now well-formed after P2415R2 relaxed + // the requirements of viewable_range to admit rvalue non-view non-borrowed + // ranges such as std::string&&. + static_assert(requires { s | lazy_split(p); }); + static_assert(requires { lazy_split(p)(s); }); + static_assert(requires { s | (lazy_split(p) | views::all); }); + static_assert(requires { (lazy_split(p) | views::all)(s); }); static_assert(requires { s | lazy_split(views::all(p)); }); static_assert(requires { lazy_split(views::all(p))(s); }); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc index 44245f5071a..ca10608efb5 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc @@ -145,10 +145,15 @@ test06() static_assert(!requires { split(p)(); }); static_assert(!requires { s | split; }); - static_assert(!requires { s | split(p); }); - static_assert(!requires { split(p)(s); }); - static_assert(!requires { s | (split(p) | views::all); }); - static_assert(!requires { (split(p) | views::all)(s); }); + // Test the case where the closure object is used as an rvalue and therefore + // the copy of p is forwarded as an rvalue. + // This used to be invalid, but is now well-formed after P2415R2 relaxed + // the requirements of viewable_range to admit rvalue non-view non-borrowed + // ranges such as std::string&&. + static_assert(requires { s | split(p); }); + static_assert(requires { split(p)(s); }); + static_assert(requires { s | (split(p) | views::all); }); + static_assert(requires { (split(p) | views::all)(s); }); static_assert(requires { s | split(views::all(p)); }); static_assert(requires { split(views::all(p))(s); });