[libcxx-commits] [libcxx] b40a3d7 - [libc++] Implement P2446R2 (views::as_rvalue)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jan 19 21:00:45 PST 2023
Author: Nikolas Klauser
Date: 2023-01-20T06:00:40+01:00
New Revision: b40a3d73dc9c10a25a72e82da70d58727d198b80
URL: https://github.com/llvm/llvm-project/commit/b40a3d73dc9c10a25a72e82da70d58727d198b80
DIFF: https://github.com/llvm/llvm-project/commit/b40a3d73dc9c10a25a72e82da70d58727d198b80.diff
LOG: [libc++] Implement P2446R2 (views::as_rvalue)
Reviewed By: ldionne, var-const, #libc
Spies: libcxx-commits
Differential Revision: https://reviews.llvm.org/D137637
Added:
libcxx/include/__ranges/as_rvalue_view.h
libcxx/test/libcxx/diagnostics/view_adaptors.nodiscard_extensions.verify.cpp
libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp
Modified:
libcxx/docs/ReleaseNotes.rst
libcxx/docs/Status/Cxx2bPapers.csv
libcxx/docs/Status/RangesViews.csv
libcxx/include/CMakeLists.txt
libcxx/include/__iterator/move_sentinel.h
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/support/test_iterators.h
Removed:
################################################################################
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index eeacec14289fd..0d5e95e5040c2 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 dd6590bda310c..487936535f349 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 eff389ae40535..3bc9c62df3b84 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 8c372a3b9b353..74d1f96326e9e 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 5adf877b3490e..0d7336a1dc2a6 100644
--- a/libcxx/include/__iterator/move_sentinel.h
+++ b/libcxx/include/__iterator/move_sentinel.h
@@ -50,6 +50,8 @@ class _LIBCPP_TEMPLATE_VIS move_sentinel
_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 0000000000000..422d8a8e08347
--- /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 a6521a937a04e..ffc8e27e86926 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 db601d48c7f10..99c16872a6078 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 0000000000000..6cd9739f054c6
--- /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 339324ca561cf..86d93b66ff52b 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 0000000000000..9b68bea0864ae
--- /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 0000000000000..dbe15238ebc86
--- /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 0000000000000..ac08f75ede504
--- /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 0000000000000..684cdd875a364
--- /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 0000000000000..5149b717337e3
--- /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 0000000000000..48321391dccb5
--- /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 0000000000000..48d1d96b02482
--- /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 0000000000000..eb79e141feb6a
--- /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 0000000000000..f330b41898b70
--- /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 b8ed6b93d08de..588a24f37ac74 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
diff erence_type = std::ptr
diff _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+(
diff erence_type n) const {
+ auto tmp = *this;
+ tmp.it += n;
+ return tmp;
+ }
+
+ constexpr friend rvalue_iterator operator+(
diff erence_type n, rvalue_iterator iter) {
+ iter += n;
+ return iter;
+ }
+
+ constexpr rvalue_iterator operator-(
diff erence_type n) const {
+ auto tmp = *this;
+ tmp.it -= n;
+ return tmp;
+ }
+
+ constexpr
diff erence_type operator-(const rvalue_iterator& other) const { return it_ - other.it_; }
+
+ constexpr rvalue_iterator& operator+=(
diff erence_type n) {
+ it_ += n;
+ return *this;
+ }
+
+ constexpr rvalue_iterator& operator-=(
diff erence_type n) {
+ it_ -= n;
+ return *this;
+ }
+
+ constexpr reference operator[](
diff erence_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
More information about the libcxx-commits
mailing list