diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -50,6 +50,7 @@ - P0600R1 - ``nodiscard`` in the library - P0339R6 - ``polymorphic_allocator<>`` as a vocabulary type - P1169R4 - ``static operator()`` +- P2446R2 - ``views::as_rvalue`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -73,7 +73,7 @@ "`P2419R2 `__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","","" "`P2438R2 `__","LWG","``std::string::substr() &&``","July 2022","|Complete|","16.0" "`P2445R1 `__","LWG","``forward_like``","July 2022","|Complete|","16.0" -"`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","","","|ranges|" +"`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0","|ranges|" "`P2460R2 `__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","","" "`P2465R3 `__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","","" "`P2467R1 `__","LWG","Support exclusive mode for ``fstreams``","July 2022","","" diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv --- a/libcxx/docs/Status/RangesViews.csv +++ b/libcxx/docs/Status/RangesViews.csv @@ -33,5 +33,5 @@ C++23,`chunk `_,Unassigned,No patch yet,Not started C++23,`chunk_by `_,Unassigned,No patch yet,Not started C++23,`as_const `_,Unassigned,No patch yet,Not started -C++23,`as_rvalue `_,Nikolas Klauser,`D137637 `_,Under review +C++23,`as_rvalue `_,Nikolas Klauser,`D137637 `_,Complete C++23,`stride `_,Unassigned,No patch yet,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -484,6 +484,7 @@ __random/weibull_distribution.h __ranges/access.h __ranges/all.h + __ranges/as_rvalue_view.h __ranges/common_view.h __ranges/concepts.h __ranges/copyable_box.h diff --git a/libcxx/include/__iterator/move_sentinel.h b/libcxx/include/__iterator/move_sentinel.h --- a/libcxx/include/__iterator/move_sentinel.h +++ b/libcxx/include/__iterator/move_sentinel.h @@ -50,6 +50,8 @@ _Sent __last_ = _Sent(); }; +_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(move_sentinel); + #endif // _LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/as_rvalue_view.h @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___RANGES_AS_RVALUE_H +#define _LIBCPP___RANGES_AS_RVALUE_H + +#include <__config> +#include <__iterator/move_iterator.h> +#include <__iterator/move_sentinel.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/view_interface.h> +#include <__utility/forward.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 23 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +template + requires input_range<_View> +class as_rvalue_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + +public: + _LIBCPP_HIDE_FROM_ABI as_rvalue_view() + requires default_initializable<_View> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit as_rvalue_view(_View __base) : __base_(std::move(__base)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return move_iterator(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return move_iterator(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View>) { + return move_iterator(ranges::end(__base_)); + } else { + return move_sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + if constexpr (common_range) { + return move_iterator(ranges::end(__base_)); + } else { + return move_sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return ranges::size(__base_); + } +}; + +template +as_rvalue_view(_Range&&) -> as_rvalue_view>; + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +namespace views { +namespace __as_rvalue { +struct __fn : __range_adaptor_closure<__fn> { + template + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(/**/ as_rvalue_view(std::forward<_Range>(__range)))) + -> decltype(/*--*/ as_rvalue_view(std::forward<_Range>(__range))) { + return /*-------------*/ as_rvalue_view(std::forward<_Range>(__range)); + } + + template + requires same_as, range_reference_t<_Range>> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(/**/ views::all(std::forward<_Range>(__range)))) + -> decltype(/*--*/ views::all(std::forward<_Range>(__range))) { + return /*-------------*/ views::all(std::forward<_Range>(__range)); + } +}; +} // namespace __as_rvalue + +inline namespace __cpo { +constexpr auto as_rvalue = __as_rvalue::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 23 + +#endif // _LIBCPP___RANGES_AS_RVALUE_H diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -271,6 +271,13 @@ (enable_borrowed_range && ...); namespace views { inline constexpr unspecified zip = unspecified; } // C++2b + + // [range.as.rvalue] + template + requires input_range + class as_rvalue_view; // since C++23 + + namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // since C++23 } namespace std { @@ -309,6 +316,7 @@ #include <__config> #include <__ranges/access.h> #include <__ranges/all.h> +#include <__ranges/as_rvalue_view.h> #include <__ranges/common_view.h> #include <__ranges/concepts.h> #include <__ranges/counted.h> diff --git a/libcxx/test/libcxx/diagnostics/view_adaptors.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/view_adaptors.nodiscard_extensions.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/view_adaptors.nodiscard_extensions.verify.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Check that view adaptors are marked [[nodiscard]] as a conforming extension + +// UNSUPPORTED: c++03, c++11, c++14 ,c++17 + +#include +#include + +void func() { + std::vector range; + + auto rvalue_view = std::views::as_rvalue(range); + std::views::as_rvalue(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::as_rvalue(rvalue_view); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -515,6 +515,7 @@ #include <__random/weibull_distribution.h> // expected-error@*:* {{use of private header from outside its module: '__random/weibull_distribution.h'}} #include <__ranges/access.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/access.h'}} #include <__ranges/all.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/all.h'}} +#include <__ranges/as_rvalue_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/as_rvalue_view.h'}} #include <__ranges/common_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/common_view.h'}} #include <__ranges/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/concepts.h'}} #include <__ranges/copyable_box.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/copyable_box.h'}} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::views::as_rvalue + +#include +#include +#include +#include + +#include "test_iterators.h" + +struct DefaultConstructibleView : std::ranges::view_base { + int i_; + int* begin(); + int* end(); +}; + +struct RValueView : std::ranges::view_base {}; + +template +concept HasPipe = requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +struct NoView {}; +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(HasPipe); +static_assert(HasPipe); +static_assert(!HasPipe); +static_assert(!HasPipe); +static_assert(std::is_same_v); + +struct move_iterator_range { + constexpr std::move_iterator begin() const { return {}; } + constexpr std::move_iterator end() const { return {}; } +}; + +static_assert(!std::ranges::view); +static_assert(std::ranges::range); + +constexpr bool test() { + { // view | views::as_rvalue + DefaultConstructibleView v{{}, 3}; + std::same_as> decltype(auto) view = v | std::views::as_rvalue; + assert(view.base().i_ == 3); + } + + { // adaptor | views::as_rvalue + DefaultConstructibleView v{{}, 3}; + const auto partial = std::views::transform(std::identity{}) | std::views::as_rvalue; + std::same_as>> decltype(auto) view = partial(v); + assert(view.base().base().i_ == 3); + } + + { // views::as_rvalue | adaptor + DefaultConstructibleView v{{}, 3}; + const auto partial = std::views::as_rvalue | std::views::transform(std::identity{}); + std::same_as, + std::identity>> decltype(auto) view = partial(v); + assert(view.base().base().i_ == 3); + } + + { // rvalue-view | views::as_rvalue + int a[4] = {1, 2, 3, 4}; + std::ranges::subrange range(rvalue_iterator(a), rvalue_iterator(a + 4)); + [[maybe_unused]] std::same_as>> decltype(auto) rval_range = + range | std::views::as_rvalue; + } + + { // range | views::as_rvalue + [[maybe_unused]] std::same_as>>> decltype(auto) + view = std::vector{} | std::views::as_rvalue; + } + + { // rvalue-range | views::as_rvalue + [[maybe_unused]] std::same_as> decltype(auto) view = + move_iterator_range{} | std::views::as_rvalue; + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr V base() const & requires copy_constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include + +#include "MoveOnly.h" + +struct SimpleView : std::ranges::view_base { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : SimpleView { + MoveOnly m; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + { // const & + const std::ranges::as_rvalue_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // & + std::ranges::as_rvalue_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // && + std::ranges::as_rvalue_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // const && + const std::ranges::as_rvalue_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // move only + std::ranges::as_rvalue_view view(MoveOnlyView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.m.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto begin() +// constexpr auto begin() const + +#include +#include +#include +#include + +#include "test_iterators.h" + +struct SimpleView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct NonSimpleView : std::ranges::view_base { + char* begin(); + char* end(); + int* begin() const; + int* end() const; +}; + +struct NonConstView : std::ranges::view_base { + char* begin(); + char* end(); +}; + +template +concept HasBegin = requires(T t) { t.begin(); }; + +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(!HasBegin>); + +template +constexpr void test_range() { + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_rvalue_view view(std::move(range)); + std::same_as> decltype(auto) iter = view.begin(); + assert(base(iter.base()) == std::begin(a)); +} + +template +class WrapRange { + Iter iter_; + Sent sent_; + +public: + constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {} + + constexpr Iter begin() const { return iter_; } + constexpr Sent end() const { return sent_; } +}; + +template +WrapRange(Iter, Sent) -> WrapRange; + +template +constexpr void test_const_range() { + int a[] = {1, 2}; + auto range = WrapRange{Iter(a), Sent(Iter(a + 2))}; + const std::ranges::as_rvalue_view view(std::views::all(range)); + std::same_as> decltype(auto) iter = view.begin(); + assert(base(iter.base()) == std::begin(a)); +} + +struct move_iterator_view : std::ranges::view_base { + constexpr std::move_iterator begin() const { return {}; } + constexpr std::move_iterator end() const { return {}; } +}; + +constexpr bool test() { + meta::for_each(meta::cpp20_input_iterator_list{}, [] { + if constexpr (std::sentinel_for) + test_range(); + test_range>(); + test_range>(); + }); + + meta::for_each(meta::forward_iterator_list{}, [] { + test_const_range(); + test_const_range>(); + test_const_range>(); + }); + + { // check that with a std::move_iterator begin() doesn't return move_iterator> + std::ranges::as_rvalue_view view{move_iterator_view{}}; + std::same_as> decltype(auto) it = view.begin(); + assert(it == std::move_iterator{}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include + +static_assert(std::is_same_v{})), + std::ranges::as_rvalue_view>>>); + +static_assert(std::is_same_v&>())), + std::ranges::as_rvalue_view&>>>); + +static_assert(std::is_same_v{})), + std::ranges::as_rvalue_view>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctor.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::ranges::as_rvalue_view::as_rvalue_view(...) + +#include +#include +#include + +struct DefaultConstructibleView : std::ranges::view_base { + int* begin() const; + int* end() const; + + int i_ = 23; +}; + +struct NonDefaultConstructibleView : std::ranges::view_base { + NonDefaultConstructibleView(int i) : i_(i) {} + + int* begin() const; + int* end() const; + + int i_ = 23; +}; + +static_assert(!std::is_constructible_v>); +static_assert(std::is_constructible_v, int>); +static_assert(std::is_nothrow_constructible_v>); + +template +concept IsImplicitlyConstructible = requires(T val, Args... args) { val = {std::forward(args)...}; }; + +static_assert(IsImplicitlyConstructible>); +static_assert(!IsImplicitlyConstructible, int>); + +constexpr bool test() { + std::ranges::as_rvalue_view view = {}; + assert(view.base().i_ == 23); + + return true; +} + +int main(int, char**) { + static_assert(test()); + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +static_assert(std::ranges::enable_borrowed_range>>); +static_assert(std::ranges::enable_borrowed_range&>>>); +static_assert(!std::ranges::enable_borrowed_range>>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto end() +// constexpr auto end() const + +#include +#include +#include + +#include "test_iterators.h" + +struct DefaultConstructibleView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct CVCallView : std::ranges::view_base { + mutable bool const_called = false; + mutable int i[1]; + constexpr int* begin() { + const_called = false; + return i; + } + + constexpr int* begin() const { + const_called = true; + return i; + } + + constexpr int* end() { + const_called = false; + return i + 1; + } + + constexpr int* end() const { + const_called = true; + return i + 1; + } +}; + +struct NonConstCommonRange : std::ranges::view_base { + int* begin(); + int* end(); + + int* begin() const; + sentinel_wrapper end() const; +}; + +struct NonConstView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +template +concept HasEnd = requires(T t) { t.end(); }; + +static_assert(HasEnd>); +static_assert(HasEnd>); +static_assert(HasEnd>); +static_assert(!HasEnd>); + +static_assert(std::is_same_v>().end()), + std::move_iterator>); +static_assert(std::is_same_v>().end()), + std::move_sentinel>>); + +template +constexpr void test_range() { + using Expected = std::conditional_t, std::move_sentinel>; + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_rvalue_view view(std::move(range)); + std::same_as decltype(auto) iter = view.end(); + assert(base(base(iter.base())) == std::end(a)); +} + +template +class WrapRange { + Iter iter_; + Sent sent_; + +public: + constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {} + + constexpr Iter begin() const { return iter_; } + constexpr Sent end() const { return sent_; } +}; + +template +WrapRange(Iter, Sent) -> WrapRange; + +template +constexpr void test_const_range() { + using Expected = std::conditional_t, std::move_sentinel>; + int a[] = {1, 2}; + auto range = WrapRange{Iter(a), Sent(Iter(a + 2))}; + const std::ranges::as_rvalue_view view(std::move(range)); + std::same_as decltype(auto) iter = view.end(); + assert(base(base(iter.base())) == std::end(a)); +} + +struct move_iterator_view : std::ranges::view_base { + constexpr std::move_iterator begin() const { return {}; } + constexpr std::move_iterator end() const { return {}; } +}; + +constexpr bool test() { + test_range, sentinel_wrapper>, false>(); + test_range, sized_sentinel>, false>(); + test_range, sentinel_wrapper>, false>(); + test_range, sized_sentinel>, false>(); + + meta::for_each(meta::forward_iterator_list{}, [] { + test_range(); + test_range, false>(); + test_range, false>(); + }); + + { + std::ranges::as_rvalue_view view(CVCallView{}); + (void)view.end(); + assert(view.base().const_called); + } + + { // check that with a std::move_iterator begin() doesn't return move_iterator> + std::ranges::as_rvalue_view view{move_iterator_view{}}; + std::same_as> decltype(auto) it = view.end(); + assert(it == std::move_iterator{}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// constexpr auto size() +// constexpr auto size() const + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include + +struct ConstSizedView : std::ranges::view_base { + bool* size_called; + int* begin() const; + int* end() const; + + constexpr size_t size() const { + *size_called = true; + return 3; + } +}; + +struct SizedView : std::ranges::view_base { + bool* size_called; + int* begin() const; + int* end() const; + + constexpr int size() { + *size_called = true; + return 5; + } +}; + +struct UnsizedView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template +concept HasSize = requires(T v) { v.size(); }; + +static_assert(HasSize); +static_assert(HasSize); +static_assert(HasSize); +static_assert(!HasSize); +static_assert(!HasSize); +static_assert(!HasSize); + +constexpr bool test() { + { + bool size_called = false; + std::ranges::as_rvalue_view view(ConstSizedView{{}, &size_called}); + std::same_as auto size = view.size(); + assert(size == 3); + assert(size_called); + } + + { + bool size_called = false; + std::ranges::as_rvalue_view view(SizedView{{}, &size_called}); + std::same_as auto size = view.size(); + assert(size == 5); + assert(size_called); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -978,6 +978,84 @@ } // namespace adl +template +class rvalue_iterator { +public: + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using reference = T&&; + using value_type = T; + + rvalue_iterator() = default; + constexpr rvalue_iterator(T* it) : it_(it) {} + + constexpr reference operator*() const { return std::move(*it_); } + + constexpr rvalue_iterator& operator++() { + ++it_; + return *this; + } + + constexpr rvalue_iterator operator++(int) { + auto tmp = *this; + ++it_; + return tmp; + } + + constexpr rvalue_iterator& operator--() { + --it_; + return *this; + } + + constexpr rvalue_iterator operator--(int) { + auto tmp = *this; + --it_; + return tmp; + } + + constexpr rvalue_iterator operator+(difference_type n) const { + auto tmp = *this; + tmp.it += n; + return tmp; + } + + constexpr friend rvalue_iterator operator+(difference_type n, rvalue_iterator iter) { + iter += n; + return iter; + } + + constexpr rvalue_iterator operator-(difference_type n) const { + auto tmp = *this; + tmp.it -= n; + return tmp; + } + + constexpr difference_type operator-(const rvalue_iterator& other) const { return it_ - other.it_; } + + constexpr rvalue_iterator& operator+=(difference_type n) { + it_ += n; + return *this; + } + + constexpr rvalue_iterator& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + constexpr reference operator[](difference_type n) const { return std::move(it_[n]); } + + auto operator<=>(const rvalue_iterator&) const noexcept = default; + +private: + T* it_; +}; + +template +rvalue_iterator(T*) -> rvalue_iterator; + +static_assert(std::random_access_iterator>); + // Proxy // ====================================================================== // Proxy that can wrap a value or a reference. It simulates C++23's tuple