aboutsummaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorNikolas Klauser <nikolasklauser@berlin.de>2022-12-11 11:32:54 +0100
committerNikolas Klauser <nikolasklauser@berlin.de>2023-01-20 06:00:40 +0100
commitb40a3d73dc9c10a25a72e82da70d58727d198b80 (patch)
tree7c7a40c6ff5fe71bf352e3fcc7092e6bc5220497 /libcxx
parent6c485409de521565aeac427cf614158b86cc7fea (diff)
downloadllvm-b40a3d73dc9c10a25a72e82da70d58727d198b80.zip
llvm-b40a3d73dc9c10a25a72e82da70d58727d198b80.tar.gz
llvm-b40a3d73dc9c10a25a72e82da70d58727d198b80.tar.bz2
[libc++] Implement P2446R2 (views::as_rvalue)
Reviewed By: ldionne, var-const, #libc Spies: libcxx-commits Differential Revision: https://reviews.llvm.org/D137637
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/docs/ReleaseNotes.rst1
-rw-r--r--libcxx/docs/Status/Cxx2bPapers.csv2
-rw-r--r--libcxx/docs/Status/RangesViews.csv2
-rw-r--r--libcxx/include/CMakeLists.txt1
-rw-r--r--libcxx/include/__iterator/move_sentinel.h2
-rw-r--r--libcxx/include/__ranges/as_rvalue_view.h137
-rw-r--r--libcxx/include/module.modulemap.in1
-rw-r--r--libcxx/include/ranges8
-rw-r--r--libcxx/test/libcxx/diagnostics/view_adaptors.nodiscard_extensions.verify.cpp22
-rw-r--r--libcxx/test/libcxx/private_headers.verify.cpp1
-rw-r--r--libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctad.compile.pass.cpp20
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp99
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp78
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp114
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp22
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctor.pass.cpp55
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp16
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp151
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp80
-rw-r--r--libcxx/test/support/test_iterators.h78
20 files changed, 888 insertions, 2 deletions
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index eeacec1..0d5e95e 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -68,6 +68,7 @@ Implemented Papers
- P0323R12 - ``std::expected``
- P1035R7 - Input Range Adaptors
- P2325R3 - Views should not be required to be default constructible
+- P2446R2 - ``views::as_rvalue``
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index dd6590b..4879365 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -73,7 +73,7 @@
"`P2419R2 <https://wg21.link/P2419R2>`__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","",""
"`P2438R2 <https://wg21.link/P2438R2>`__","LWG","``std::string::substr() &&``","July 2022","|Complete|","16.0"
"`P2445R1 <https://wg21.link/P2445R1>`__","LWG","``forward_like``","July 2022","|Complete|","16.0"
-"`P2446R2 <https://wg21.link/P2446R2>`__","LWG","``views::as_rvalue``","July 2022","","","|ranges|"
+"`P2446R2 <https://wg21.link/P2446R2>`__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0","|ranges|"
"`P2460R2 <https://wg21.link/P2460R2>`__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","",""
"`P2465R3 <https://wg21.link/P2465R3>`__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","",""
"`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","",""
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index eff389a..3bc9c62 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -33,5 +33,5 @@ C++23,`slide <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk_by <https://wg21.link/P2443R1>`_,Unassigned,No patch yet,Not started
C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started
-C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,Under review
+C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,Complete
C++23,`stride <https://wg21.link/P1899R3>`_,Unassigned,No patch yet,Not started
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8c372a3b..74d1f96 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -499,6 +499,7 @@ set(files
__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
index 5adf877..0d7336a 100644
--- a/libcxx/include/__iterator/move_sentinel.h
+++ b/libcxx/include/__iterator/move_sentinel.h
@@ -50,6 +50,8 @@ private:
_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
index 0000000..422d8a8
--- /dev/null
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__concepts/constructible.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__iterator/move_iterator.h>
+#include <__iterator/move_sentinel.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.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 <view _View>
+ requires input_range<_View>
+class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
+ _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<const _View>
+ {
+ 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<const _View>
+ {
+ if constexpr (common_range<const _View>) {
+ 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<const _View>
+ {
+ return ranges::size(__base_);
+ }
+};
+
+template <class _Range>
+as_rvalue_view(_Range&&) -> as_rvalue_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<as_rvalue_view<_View>> = enable_borrowed_range<_View>;
+
+namespace views {
+namespace __as_rvalue {
+struct __fn : __range_adaptor_closure<__fn> {
+ template <class _Range>
+ _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 <class _Range>
+ requires same_as<range_rvalue_reference_t<_Range>, 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/module.modulemap.in b/libcxx/include/module.modulemap.in
index a6521a9..ffc8e27 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1221,6 +1221,7 @@ module std [system] {
export functional.__functional.compose
export functional.__functional.perfect_forward
}
+ module as_rvalue_view { private header "__ranges/as_rvalue_view.h" }
module common_view { private header "__ranges/common_view.h" }
module concepts { private header "__ranges/concepts.h" }
module copyable_box { private header "__ranges/copyable_box.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index db601d4..99c1687 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -292,6 +292,13 @@ namespace std::ranges {
(enable_borrowed_range<Views> && ...);
namespace views { inline constexpr unspecified zip = unspecified; } // C++2b
+
+ // [range.as.rvalue]
+ template <view V>
+ requires input_range<V>
+ class as_rvalue_view; // since C++23
+
+ namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // since C++23
}
namespace std {
@@ -330,6 +337,7 @@ namespace std {
#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
index 0000000..6cd9739
--- /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, c++20
+
+#include <ranges>
+#include <vector>
+
+void func() {
+ std::vector<int> 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
index 339324c..86d93b6 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -530,6 +530,7 @@ END-SCRIPT
#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/iterators/predef.iterators/move.iterators/move.sentinel/ctad.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctad.compile.pass.cpp
new file mode 100644
index 0000000..9b68bea
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctad.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <iterator>
+#include <type_traits>
+
+#include <test_iterators.h>
+
+static_assert(std::is_same_v<decltype(std::move_sentinel(std::default_sentinel_t{})),
+ std::move_sentinel<std::default_sentinel_t>>);
+
+static_assert(
+ std::is_same_v<decltype(std::move_sentinel(sentinel_wrapper<int*>{})), std::move_sentinel<sentinel_wrapper<int*>>>);
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
index 0000000..dbe1523
--- /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 <cassert>
+#include <functional>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+
+struct DefaultConstructibleView : std::ranges::view_base {
+ int i_;
+ int* begin();
+ int* end();
+};
+
+struct RValueView : std::ranges::view_base {};
+
+template <class View, class T>
+concept HasPipe = requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct NoView {};
+static_assert(std::is_invocable_v<decltype(std::views::as_rvalue), DefaultConstructibleView>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_rvalue)>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_rvalue), NoView>);
+static_assert(HasPipe<DefaultConstructibleView&, decltype(std::views::as_rvalue)>);
+static_assert(HasPipe<int (&)[10], decltype(std::views::as_rvalue)>);
+static_assert(!HasPipe<int (&&)[10], decltype(std::views::as_rvalue)>);
+static_assert(!HasPipe<NoView, decltype(std::views::as_rvalue)>);
+static_assert(std::is_same_v<decltype(std::views::as_rvalue), decltype(std::ranges::views::as_rvalue)>);
+
+struct move_iterator_range {
+ constexpr std::move_iterator<int*> begin() const { return {}; }
+ constexpr std::move_iterator<int*> end() const { return {}; }
+};
+
+static_assert(!std::ranges::view<move_iterator_range>);
+static_assert(std::ranges::range<move_iterator_range>);
+
+constexpr bool test() {
+ { // view | views::as_rvalue
+ DefaultConstructibleView v{{}, 3};
+ std::same_as<std::ranges::as_rvalue_view<DefaultConstructibleView>> 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<std::ranges::as_rvalue_view<
+ std::ranges::transform_view<DefaultConstructibleView, std::identity>>> 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::ranges::transform_view<std::ranges::as_rvalue_view<DefaultConstructibleView>,
+ 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<std::ranges::subrange<rvalue_iterator<int>>> decltype(auto) rval_range =
+ range | std::views::as_rvalue;
+ }
+
+ { // range | views::as_rvalue
+ [[maybe_unused]] std::same_as<std::ranges::as_rvalue_view<std::views::all_t<std::vector<int>>>> decltype(auto)
+ view = std::vector<int>{} | std::views::as_rvalue;
+ }
+
+ { // rvalue-range | views::as_rvalue
+ [[maybe_unused]] std::same_as<std::views::all_t<move_iterator_range>> 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
index 0000000..ac08f75
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct SimpleView : std::ranges::view_base {
+ int i;
+ int* begin() const;
+ int* end() const;
+};
+
+struct MoveOnlyView : SimpleView {
+ MoveOnly m;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::as_rvalue_view<SimpleView> const&>);
+static_assert(HasBase<std::ranges::as_rvalue_view<SimpleView>&&>);
+
+static_assert(!HasBase<std::ranges::as_rvalue_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::as_rvalue_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+ { // const &
+ const std::ranges::as_rvalue_view<SimpleView> view(SimpleView{{}, 5});
+ std::same_as<SimpleView> decltype(auto) v = view.base();
+ assert(v.i == 5);
+ }
+
+ { // &
+ std::ranges::as_rvalue_view<SimpleView> view(SimpleView{{}, 5});
+ std::same_as<SimpleView> decltype(auto) v = view.base();
+ assert(v.i == 5);
+ }
+
+ { // &&
+ std::ranges::as_rvalue_view<SimpleView> view(SimpleView{{}, 5});
+ std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+ assert(v.i == 5);
+ }
+
+ { // const &&
+ const std::ranges::as_rvalue_view<SimpleView> view(SimpleView{{}, 5});
+ std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+ assert(v.i == 5);
+ }
+
+ { // move only
+ std::ranges::as_rvalue_view<MoveOnlyView> view(MoveOnlyView{{}, 5});
+ std::same_as<MoveOnlyView> 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
index 0000000..684cdd8
--- /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 <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#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 <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+static_assert(HasBegin<std::ranges::as_rvalue_view<SimpleView>>);
+static_assert(HasBegin<const std::ranges::as_rvalue_view<SimpleView>>);
+static_assert(HasBegin<std::ranges::as_rvalue_view<NonSimpleView>>);
+static_assert(HasBegin<const std::ranges::as_rvalue_view<NonSimpleView>>);
+static_assert(HasBegin<std::ranges::as_rvalue_view<NonConstView>>);
+static_assert(!HasBegin<const std::ranges::as_rvalue_view<NonConstView>>);
+
+template <class Iter, class Sent>
+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<std::move_iterator<Iter>> decltype(auto) iter = view.begin();
+ assert(base(iter.base()) == std::begin(a));
+}
+
+template <class Iter, class Sent>
+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 <class Iter, class Sent>
+WrapRange(Iter, Sent) -> WrapRange<Iter, Sent>;
+
+template <class Iter, class Sent>
+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<std::move_iterator<Iter>> decltype(auto) iter = view.begin();
+ assert(base(iter.base()) == std::begin(a));
+}
+
+struct move_iterator_view : std::ranges::view_base {
+ constexpr std::move_iterator<int*> begin() const { return {}; }
+ constexpr std::move_iterator<int*> end() const { return {}; }
+};
+
+constexpr bool test() {
+ meta::for_each(meta::cpp20_input_iterator_list<int*>{}, []<class Iter> {
+ if constexpr (std::sentinel_for<Iter, Iter>)
+ test_range<Iter, Iter>();
+ test_range<Iter, sentinel_wrapper<Iter>>();
+ test_range<Iter, sized_sentinel<Iter>>();
+ });
+
+ meta::for_each(meta::forward_iterator_list<const int*>{}, []<class Iter> {
+ test_const_range<Iter, Iter>();
+ test_const_range<Iter, sentinel_wrapper<Iter>>();
+ test_const_range<Iter, sized_sentinel<Iter>>();
+ });
+
+ { // check that with a std::move_iterator begin() doesn't return move_iterator<move_iterator<T>>
+ std::ranges::as_rvalue_view view{move_iterator_view{}};
+ std::same_as<std::move_iterator<int*>> decltype(auto) it = view.begin();
+ assert(it == std::move_iterator<int*>{});
+ }
+
+ 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
index 0000000..5149b71
--- /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 <ranges>
+#include <vector>
+#include <utility>
+
+static_assert(std::is_same_v<decltype(std::ranges::as_rvalue_view(std::vector<int>{})),
+ std::ranges::as_rvalue_view<std::views::all_t<std::vector<int>>>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::as_rvalue_view(std::declval<std::vector<int>&>())),
+ std::ranges::as_rvalue_view<std::views::all_t<std::vector<int>&>>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::as_rvalue_view(std::ranges::empty_view<int>{})),
+ std::ranges::as_rvalue_view<std::ranges::empty_view<int>>>);
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
index 0000000..4832139
--- /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 <cassert>
+#include <ranges>
+#include <vector>
+
+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<std::ranges::as_rvalue_view<NonDefaultConstructibleView>>);
+static_assert(std::is_constructible_v<std::ranges::as_rvalue_view<NonDefaultConstructibleView>, int>);
+static_assert(std::is_nothrow_constructible_v<std::ranges::as_rvalue_view<DefaultConstructibleView>>);
+
+template <class T, class... Args>
+concept IsImplicitlyConstructible = requires(T val, Args... args) { val = {std::forward<Args>(args)...}; };
+
+static_assert(IsImplicitlyConstructible<std::ranges::as_rvalue_view<DefaultConstructibleView>>);
+static_assert(!IsImplicitlyConstructible<std::ranges::as_rvalue_view<NonDefaultConstructibleView>, int>);
+
+constexpr bool test() {
+ std::ranges::as_rvalue_view<DefaultConstructibleView> 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
index 0000000..48d1d96
--- /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 <ranges>
+#include <vector>
+
+static_assert(std::ranges::enable_borrowed_range<std::ranges::as_rvalue_view<std::ranges::empty_view<int>>>);
+static_assert(std::ranges::enable_borrowed_range<std::ranges::as_rvalue_view<std::views::all_t<std::vector<int>&>>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::as_rvalue_view<std::views::all_t<std::vector<int>>>>);
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
index 0000000..eb79e14
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp
@@ -0,0 +1,151 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#include <cassert>
+#include <ranges>
+
+#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<int*> end() const;
+};
+
+struct NonConstView : std::ranges::view_base {
+ int* begin();
+ int* end();
+};
+
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+static_assert(HasEnd<std::ranges::as_rvalue_view<DefaultConstructibleView>>);
+static_assert(HasEnd<const std::ranges::as_rvalue_view<DefaultConstructibleView>>);
+static_assert(HasEnd<std::ranges::as_rvalue_view<NonConstView>>);
+static_assert(!HasEnd<const std::ranges::as_rvalue_view<NonConstView>>);
+
+static_assert(std::is_same_v<decltype(std::declval<std::ranges::as_rvalue_view<DefaultConstructibleView>>().end()),
+ std::move_iterator<int*>>);
+static_assert(std::is_same_v<decltype(std::declval<const std::ranges::as_rvalue_view<NonConstCommonRange>>().end()),
+ std::move_sentinel<sentinel_wrapper<int*>>>);
+
+template <class Iter, class Sent, bool is_common>
+constexpr void test_range() {
+ using Expected = std::conditional_t<is_common, std::move_iterator<Sent>, std::move_sentinel<Sent>>;
+ 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<Expected> decltype(auto) iter = view.end();
+ assert(base(base(iter.base())) == std::end(a));
+}
+
+template <class Iter, class Sent>
+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 <class Iter, class Sent>
+WrapRange(Iter, Sent) -> WrapRange<Iter, Sent>;
+
+template <class Iter, class Sent, bool is_common>
+constexpr void test_const_range() {
+ using Expected = std::conditional_t<is_common, std::move_iterator<Sent>, std::move_sentinel<Sent>>;
+ 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<Expected> 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<int*> begin() const { return {}; }
+ constexpr std::move_iterator<int*> end() const { return {}; }
+};
+
+constexpr bool test() {
+ test_range<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>, false>();
+ test_range<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>, false>();
+ test_range<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>, false>();
+ test_range<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>, false>();
+
+ meta::for_each(meta::forward_iterator_list<int*>{}, []<class Iter> {
+ test_range<Iter, Iter, true>();
+ test_range<Iter, sentinel_wrapper<Iter>, false>();
+ test_range<Iter, sized_sentinel<Iter>, 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<move_iterator<T>>
+ std::ranges::as_rvalue_view view{move_iterator_view{}};
+ std::same_as<std::move_iterator<int*>> decltype(auto) it = view.end();
+ assert(it == std::move_iterator<int*>{});
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+
+ 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
index 0000000..f330b41
--- /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 <cassert>
+#include <cstddef>
+#include <ranges>
+
+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 <class T>
+concept HasSize = requires(T v) { v.size(); };
+
+static_assert(HasSize<ConstSizedView>);
+static_assert(HasSize<const ConstSizedView>);
+static_assert(HasSize<SizedView>);
+static_assert(!HasSize<const SizedView>);
+static_assert(!HasSize<UnsizedView>);
+static_assert(!HasSize<const UnsizedView>);
+
+constexpr bool test() {
+ {
+ bool size_called = false;
+ std::ranges::as_rvalue_view view(ConstSizedView{{}, &size_called});
+ std::same_as<size_t> 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<int> 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
index b8ed6b9..588a24f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1005,6 +1005,84 @@ class Iterator {
} // namespace adl
+template <class T>
+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 <class T>
+rvalue_iterator(T*) -> rvalue_iterator<T>;
+
+static_assert(std::random_access_iterator<rvalue_iterator<int*>>);
+
// Proxy
// ======================================================================
// Proxy that can wrap a value or a reference. It simulates C++23's tuple