[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