[llvm] 9f6439f - [libc++][ranges] Implement P2494R2 (Relaxing range adaptors to allow for move only types)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 24 17:16:01 PDT 2023


Author: yronglin
Date: 2023-06-25T08:15:52+08:00
New Revision: 9f6439f1c5a3e98d03b7416449a256062e420003

URL: https://github.com/llvm/llvm-project/commit/9f6439f1c5a3e98d03b7416449a256062e420003
DIFF: https://github.com/llvm/llvm-project/commit/9f6439f1c5a3e98d03b7416449a256062e420003.diff

LOG: [libc++][ranges] Implement P2494R2 (Relaxing range adaptors to allow for move only types)

Implement P2494R2 `Relaxing range adaptors to allow for move only types`

https://open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2494r2.html#wording-ftm

According to the words in P2494R2, I haven't add new test for `drop_while_view`, `take_while_view` and `filter_view`, because these views has the requirement that the predicate is an `indirect_unary_predicate`, which requires that the predicate is `copy_constructible`, so they still can't accept move only types as predicate.

```
[P2483R0] also suggests future work to relax the requirements on the predicate types stored by standard views. This paper does not perform this relaxation, as the copy constructibility requirement is enshrined in the indirect callable concepts ([indirectcallable.indirectinvocable]). Thus, while this paper modifies the views that currently use copyable-box for user provided predicates, it only does so to apply the rename of the exposition-only type to movable-box; it does not change any of the constraints on those views. It does, however, relax the requirements on invocables accepted by the transform family of views, because those are not constrained using the indirect callable concepts.
```

Reviewed By: #libc, var-const

Differential Revision: https://reviews.llvm.org/D151629

Added: 
    libcxx/include/__ranges/movable_box.h
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/Status/Cxx23Papers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__ranges/drop_while_view.h
    libcxx/include/__ranges/filter_view.h
    libcxx/include/__ranges/iota_view.h
    libcxx/include/__ranges/single_view.h
    libcxx/include/__ranges/take_while_view.h
    libcxx/include/__ranges/transform_view.h
    libcxx/include/module.modulemap.in
    libcxx/include/version
    libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
    libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp
    libcxx/utils/data/ignore_format.txt
    libcxx/utils/generate_feature_test_macro_components.py
    llvm/utils/gn/secondary/libcxx/include/BUILD.gn

Removed: 
    libcxx/include/__ranges/copyable_box.h
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 9fa449557ec06..deb3b3dacebd1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -264,7 +264,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_polymorphic_allocator``                 ``201902L``
     --------------------------------------------------- -----------------
-    ``__cpp_lib_ranges``                                ``202106L``
+    ``__cpp_lib_ranges``                                ``202207L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_remove_cvref``                          ``201711L``
     --------------------------------------------------- -----------------

diff  --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index c38327b187c1e..034ea99d1cfc6 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -78,7 +78,7 @@
 "`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","",""
 "`P2474R2 <https://wg21.link/P2474R2>`__","LWG","``views::repeat``","July 2022","","","|ranges|"
-"`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","","","|ranges|"
+"`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|"
 "`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|"
 "`P2502R2 <https://wg21.link/P2502R2>`__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","","","|ranges|"
 "`P2508R1 <https://wg21.link/P2508R1>`__","LWG","Exposing ``std::basic-format-string``","July 2022","|Complete|","15.0"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a8e40112df36d..7de36c005c28a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -609,7 +609,6 @@ set(files
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
-  __ranges/copyable_box.h
   __ranges/counted.h
   __ranges/dangling.h
   __ranges/data.h
@@ -626,6 +625,7 @@ set(files
   __ranges/istream_view.h
   __ranges/join_view.h
   __ranges/lazy_split_view.h
+  __ranges/movable_box.h
   __ranges/non_propagating_cache.h
   __ranges/owning_view.h
   __ranges/range_adaptor.h

diff  --git a/libcxx/include/__ranges/copyable_box.h b/libcxx/include/__ranges/copyable_box.h
deleted file mode 100644
index 9fbbb1eae90eb..0000000000000
--- a/libcxx/include/__ranges/copyable_box.h
+++ /dev/null
@@ -1,182 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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_COPYABLE_BOX_H
-#define _LIBCPP___RANGES_COPYABLE_BOX_H
-
-#include <__concepts/constructible.h>
-#include <__concepts/copyable.h>
-#include <__concepts/movable.h>
-#include <__config>
-#include <__memory/addressof.h>
-#include <__memory/construct_at.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_nothrow_copy_constructible.h>
-#include <__type_traits/is_nothrow_default_constructible.h>
-#include <__utility/move.h>
-#include <optional>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-#if _LIBCPP_STD_VER >= 20
-
-// __copyable_box allows turning a type that is copy-constructible (but maybe not copy-assignable) into
-// a type that is both copy-constructible and copy-assignable. It does that by introducing an empty state
-// and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary
-// to handle the case where the copy construction fails after destroying the object.
-//
-// In some cases, we can completely avoid the use of an empty state; we provide a specialization of
-// __copyable_box that does this, see below for the details.
-
-template<class _Tp>
-concept __copy_constructible_object = copy_constructible<_Tp> && is_object_v<_Tp>;
-
-namespace ranges {
-  // Primary template - uses std::optional and introduces an empty state in case assignment fails.
-  template<__copy_constructible_object _Tp>
-  class __copyable_box {
-    _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_;
-
-  public:
-    template<class ..._Args>
-      requires is_constructible_v<_Tp, _Args...>
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr explicit __copyable_box(in_place_t, _Args&& ...__args)
-      noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
-      : __val_(in_place, std::forward<_Args>(__args)...)
-    { }
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
-      requires default_initializable<_Tp>
-      : __val_(in_place)
-    { }
-
-    _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box const&) = default;
-    _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box&&) = default;
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box& operator=(__copyable_box const& __other)
-      noexcept(is_nothrow_copy_constructible_v<_Tp>)
-    {
-      if (this != std::addressof(__other)) {
-        if (__other.__has_value()) __val_.emplace(*__other);
-        else                       __val_.reset();
-      }
-      return *this;
-    }
-
-    _LIBCPP_HIDE_FROM_ABI
-    __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default;
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box& operator=(__copyable_box&& __other)
-      noexcept(is_nothrow_move_constructible_v<_Tp>)
-    {
-      if (this != std::addressof(__other)) {
-        if (__other.__has_value()) __val_.emplace(std::move(*__other));
-        else                       __val_.reset();
-      }
-      return *this;
-    }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; }
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr const _Tp *operator->() const noexcept { return __val_.operator->(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp *operator->() noexcept { return __val_.operator->(); }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); }
-  };
-
-  // This partial specialization implements an optimization for when we know we don't need to store
-  // an empty state to represent failure to perform an assignment. For copy-assignment, this happens:
-  //
-  // 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator
-  //    directly and avoid using std::optional.
-  // 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as
-  //    destroy-and-then-construct and we know it will never fail, so we don't need an empty state.
-  //
-  // The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and
-  // nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled
-  // whenever we can apply any of these optimizations for both the copy assignment and the move assignment
-  // operator.
-  template<class _Tp>
-  concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>;
-
-  template<class _Tp>
-  concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>;
-
-  template<__copy_constructible_object _Tp>
-    requires __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>
-  class __copyable_box<_Tp> {
-    _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_;
-
-  public:
-    template<class ..._Args>
-      requires is_constructible_v<_Tp, _Args...>
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr explicit __copyable_box(in_place_t, _Args&& ...__args)
-      noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
-      : __val_(std::forward<_Args>(__args)...)
-    { }
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
-      requires default_initializable<_Tp>
-      : __val_()
-    { }
-
-    _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box const&) = default;
-    _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box&&) = default;
-
-    // Implementation of assignment operators in case we perform optimization (1)
-    _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box const&) requires copyable<_Tp> = default;
-    _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default;
-
-    // Implementation of assignment operators in case we perform optimization (2)
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box& operator=(__copyable_box const& __other) noexcept {
-      static_assert(is_nothrow_copy_constructible_v<_Tp>);
-      if (this != std::addressof(__other)) {
-        std::destroy_at(std::addressof(__val_));
-        std::construct_at(std::addressof(__val_), __other.__val_);
-      }
-      return *this;
-    }
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr __copyable_box& operator=(__copyable_box&& __other) noexcept {
-      static_assert(is_nothrow_move_constructible_v<_Tp>);
-      if (this != std::addressof(__other)) {
-        std::destroy_at(std::addressof(__val_));
-        std::construct_at(std::addressof(__val_), std::move(__other.__val_));
-      }
-      return *this;
-    }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; }
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr const _Tp *operator->() const noexcept { return std::addressof(__val_); }
-    _LIBCPP_HIDE_FROM_ABI constexpr _Tp *operator->() noexcept { return std::addressof(__val_); }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; }
-  };
-} // namespace ranges
-
-#endif // _LIBCPP_STD_VER >= 20
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___RANGES_COPYABLE_BOX_H

diff  --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h
index 518feae4e2a98..2f5fdb5f8a1cf 100644
--- a/libcxx/include/__ranges/drop_while_view.h
+++ b/libcxx/include/__ranges/drop_while_view.h
@@ -20,8 +20,8 @@
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
-#include <__ranges/copyable_box.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
@@ -82,7 +82,7 @@ class drop_while_view : public view_interface<drop_while_view<_View, _Pred>> {
 
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
-  _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_;
 
   static constexpr bool _UseCache = forward_range<_View>;
   using _Cache                    = _If<_UseCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;

diff  --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 4acbfad54cc58..77c3553955dc7 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -28,7 +28,7 @@
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
-#include <__ranges/copyable_box.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
@@ -53,7 +53,7 @@ namespace ranges {
     requires view<_View> && is_object_v<_Pred>
   class filter_view : public view_interface<filter_view<_View, _Pred>> {
     _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
-    _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+    _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_;
 
     // We cache the result of begin() to allow providing an amortized O(1) begin() whenever
     // the underlying range is at least a forward_range.

diff  --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index ebfa0220070a5..a08fde2ab6507 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -27,8 +27,8 @@
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/unreachable_sentinel.h>
-#include <__ranges/copyable_box.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_nothrow_copy_constructible.h>

diff  --git a/libcxx/include/__ranges/movable_box.h b/libcxx/include/__ranges/movable_box.h
new file mode 100644
index 0000000000000..8b3716a06c5b7
--- /dev/null
+++ b/libcxx/include/__ranges/movable_box.h
@@ -0,0 +1,206 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_MOVABLE_BOX_H
+#define _LIBCPP___RANGES_MOVABLE_BOX_H
+
+#include <__concepts/constructible.h>
+#include <__concepts/copyable.h>
+#include <__concepts/movable.h>
+#include <__config>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_nothrow_copy_constructible.h>
+#include <__type_traits/is_nothrow_default_constructible.h>
+#include <__utility/move.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+// __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into
+// a type that is both move-constructible and move-assignable. It does that by introducing an empty state
+// and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary
+// to handle the case where the copy construction fails after destroying the object.
+//
+// In some cases, we can completely avoid the use of an empty state; we provide a specialization of
+// __movable_box that does this, see below for the details.
+
+// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not
+// just move-constructible; we preserve the old behavior in pre-C++23 modes.
+template <class _Tp>
+concept __movable_box_object =
+#  if _LIBCPP_STD_VER >= 23
+    move_constructible<_Tp>
+#  else
+    copy_constructible<_Tp>
+#  endif
+    && is_object_v<_Tp>;
+
+namespace ranges {
+// Primary template - uses std::optional and introduces an empty state in case assignment fails.
+template <__movable_box_object _Tp>
+class __movable_box {
+  _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_;
+
+public:
+  template <class... _Args>
+    requires is_constructible_v<_Tp, _Args...>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept(
+      is_nothrow_constructible_v<_Tp, _Args...>)
+      : __val_(in_place, std::forward<_Args>(__args)...) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
+    requires default_initializable<_Tp>
+      : __val_(in_place) {}
+
+  _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default;
+  _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box&
+  operator=(__movable_box const& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>)
+#  if _LIBCPP_STD_VER >= 23
+    requires copy_constructible<_Tp>
+#  endif
+  {
+    if (this != std::addressof(__other)) {
+      if (__other.__has_value())
+        __val_.emplace(*__other);
+      else
+        __val_.reset();
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&)
+    requires movable<_Tp>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box&
+  operator=(__movable_box&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) {
+    if (this != std::addressof(__other)) {
+      if (__other.__has_value())
+        __val_.emplace(std::move(*__other));
+      else
+        __val_.reset();
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return __val_.operator->(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return __val_.operator->(); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); }
+};
+
+// This partial specialization implements an optimization for when we know we don't need to store
+// an empty state to represent failure to perform an assignment. For copy-assignment, this happens:
+//
+// 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator
+//    directly and avoid using std::optional.
+// 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as
+//    destroy-and-then-construct and we know it will never fail, so we don't need an empty state.
+//
+// The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and
+// nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled
+// whenever we can apply any of these optimizations for both the copy assignment and the move assignment
+// operator.
+
+#  if _LIBCPP_STD_VER >= 23
+template <class _Tp>
+concept __doesnt_need_empty_state =
+    (copy_constructible<_Tp>
+         // 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models
+         //    copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true.
+         ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>)
+         // 2. Otherwise, movable-box<T> should store only a T if either T models movable or
+         //    is_nothrow_move_constructible_v<T> is true.
+         : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>);
+#  else
+
+template <class _Tp>
+concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>;
+
+template <class _Tp>
+concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>;
+
+template <class _Tp>
+concept __doesnt_need_empty_state = __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>;
+#  endif
+
+template <__movable_box_object _Tp>
+  requires __doesnt_need_empty_state<_Tp>
+class __movable_box<_Tp> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_;
+
+public:
+  template <class... _Args>
+    requires is_constructible_v<_Tp, _Args...>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept(
+      is_nothrow_constructible_v<_Tp, _Args...>)
+      : __val_(std::forward<_Args>(__args)...) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
+    requires default_initializable<_Tp>
+      : __val_() {}
+
+  _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default;
+  _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&)      = default;
+
+  // Implementation of assignment operators in case we perform optimization (1)
+  _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box const&)
+    requires copyable<_Tp>
+  = default;
+  _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&)
+    requires movable<_Tp>
+  = default;
+
+  // Implementation of assignment operators in case we perform optimization (2)
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box const& __other) noexcept {
+    static_assert(is_nothrow_copy_constructible_v<_Tp>);
+    if (this != std::addressof(__other)) {
+      std::destroy_at(std::addressof(__val_));
+      std::construct_at(std::addressof(__val_), __other.__val_);
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box&& __other) noexcept {
+    static_assert(is_nothrow_move_constructible_v<_Tp>);
+    if (this != std::addressof(__other)) {
+      std::destroy_at(std::addressof(__val_));
+      std::construct_at(std::addressof(__val_), std::move(__other.__val_));
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return std::addressof(__val_); }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return std::addressof(__val_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; }
+};
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_MOVABLE_BOX_H

diff  --git a/libcxx/include/__ranges/single_view.h b/libcxx/include/__ranges/single_view.h
index ccab2c1664ac7..5724e2d1b6d5f 100644
--- a/libcxx/include/__ranges/single_view.h
+++ b/libcxx/include/__ranges/single_view.h
@@ -12,7 +12,7 @@
 
 #include <__concepts/constructible.h>
 #include <__config>
-#include <__ranges/copyable_box.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
@@ -31,48 +31,48 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 20
 
 namespace ranges {
-  template<copy_constructible _Tp>
-    requires is_object_v<_Tp>
-  class single_view : public view_interface<single_view<_Tp>> {
-    __copyable_box<_Tp> __value_;
-
-  public:
-    _LIBCPP_HIDE_FROM_ABI
-    single_view() requires default_initializable<_Tp> = default;
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr explicit single_view(const _Tp& __t) : __value_(in_place, __t) {}
-
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr explicit single_view(_Tp&& __t) : __value_(in_place, std::move(__t)) {}
-
-    template<class... _Args>
-      requires constructible_from<_Tp, _Args...>
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr explicit single_view(in_place_t, _Args&&... __args)
+#  if _LIBCPP_STD_VER >= 23
+template <move_constructible _Tp>
+#  else
+template <copy_constructible _Tp>
+#  endif
+  requires is_object_v<_Tp>
+class single_view : public view_interface<single_view<_Tp>> {
+  __movable_box<_Tp> __value_;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI single_view()
+    requires default_initializable<_Tp>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit single_view(const _Tp& __t)
+#  if _LIBCPP_STD_VER >= 23
+    requires copy_constructible<_Tp>
+#  endif
+      : __value_(in_place, __t) {
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit single_view(_Tp&& __t) : __value_(in_place, std::move(__t)) {}
+
+  template <class... _Args>
+    requires constructible_from<_Tp, _Args...>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit single_view(in_place_t, _Args&&... __args)
       : __value_{in_place, std::forward<_Args>(__args)...} {}
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr _Tp* begin() noexcept { return data(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp* begin() noexcept { return data(); }
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr const _Tp* begin() const noexcept { return data(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* begin() const noexcept { return data(); }
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr _Tp* end() noexcept { return data() + 1; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp* end() noexcept { return data() + 1; }
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr const _Tp* end() const noexcept { return data() + 1; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* end() const noexcept { return data() + 1; }
 
-    _LIBCPP_HIDE_FROM_ABI
-    static constexpr size_t size() noexcept { return 1; }
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t size() noexcept { return 1; }
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr _Tp* data() noexcept { return __value_.operator->(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp* data() noexcept { return __value_.operator->(); }
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr const _Tp* data() const noexcept { return __value_.operator->(); }
-  };
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* data() const noexcept { return __value_.operator->(); }
+};
 
 template<class _Tp>
 single_view(_Tp) -> single_view<_Tp>;

diff  --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index d1f1bfe75411f..b4bdd1865de1c 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -20,7 +20,7 @@
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
-#include <__ranges/copyable_box.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
@@ -60,7 +60,7 @@ class take_while_view : public view_interface<take_while_view<_View, _Pred>> {
   class __sentinel;
 
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
-  _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_;
 
 public:
   _LIBCPP_HIDE_FROM_ABI take_while_view()

diff  --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 8bdfa97355476..dd346fb25be5e 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -26,8 +26,8 @@
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
-#include <__ranges/copyable_box.h>
 #include <__ranges/empty.h>
+#include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
@@ -62,13 +62,17 @@ concept __transform_view_constraints =
   regular_invocable<_Fn&, range_reference_t<_View>> &&
   __can_reference<invoke_result_t<_Fn&, range_reference_t<_View>>>;
 
-template<input_range _View, copy_constructible _Fn>
+#  if _LIBCPP_STD_VER >= 23
+template <input_range _View, move_constructible _Fn>
+#  else
+template <input_range _View, copy_constructible _Fn>
+#  endif
   requires __transform_view_constraints<_View, _Fn>
 class transform_view : public view_interface<transform_view<_View, _Fn>> {
   template<bool> class __iterator;
   template<bool> class __sentinel;
 
-  _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Fn> __func_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Fn> __func_;
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 
 public:
@@ -161,11 +165,14 @@ struct __transform_view_iterator_category_base<_View, _Fn> {
   >;
 };
 
-template<input_range _View, copy_constructible _Fn>
+#  if _LIBCPP_STD_VER >= 23
+template <input_range _View, move_constructible _Fn>
+#  else
+template <input_range _View, copy_constructible _Fn>
+#  endif
   requires __transform_view_constraints<_View, _Fn>
-template<bool _Const>
-class transform_view<_View, _Fn>::__iterator
-  : public __transform_view_iterator_category_base<_View, _Fn> {
+template <bool _Const>
+class transform_view<_View, _Fn>::__iterator : public __transform_view_iterator_category_base<_View, _Fn> {
 
   using _Parent = __maybe_const<_Const, transform_view>;
   using _Base = __maybe_const<_Const, _View>;
@@ -357,9 +364,13 @@ class transform_view<_View, _Fn>::__iterator
   }
 };
 
-template<input_range _View, copy_constructible _Fn>
+#  if _LIBCPP_STD_VER >= 23
+template <input_range _View, move_constructible _Fn>
+#  else
+template <input_range _View, copy_constructible _Fn>
+#  endif
   requires __transform_view_constraints<_View, _Fn>
-template<bool _Const>
+template <bool _Const>
 class transform_view<_View, _Fn>::__sentinel {
   using _Parent = __maybe_const<_Const, transform_view>;
   using _Base = __maybe_const<_Const, _View>;

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cdfb0e9d1534a..d49e339f2fbb7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1359,7 +1359,6 @@ module std [system] {
       module common_view                { private header "__ranges/common_view.h" }
       module concepts                   { private header "__ranges/concepts.h" }
       module container_compatible_range { private header "__ranges/container_compatible_range.h" }
-      module copyable_box               { private header "__ranges/copyable_box.h" }
       module counted                    {
         private header "__ranges/counted.h"
         export span
@@ -1382,6 +1381,7 @@ module std [system] {
       }
       module join_view                  { private header "__ranges/join_view.h" }
       module lazy_split_view            { private header "__ranges/lazy_split_view.h" }
+      module movable_box                { private header "__ranges/movable_box.h" }
       module non_propagating_cache      { private header "__ranges/non_propagating_cache.h" }
       module owning_view                { private header "__ranges/owning_view.h" }
       module range_adaptor              { private header "__ranges/range_adaptor.h" }

diff  --git a/libcxx/include/version b/libcxx/include/version
index ac932062aacf9..4091a491d9fad 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -150,7 +150,7 @@ __cpp_lib_out_ptr                                       202106L <memory>
 __cpp_lib_parallel_algorithm                            201603L <algorithm> <numeric>
 __cpp_lib_polymorphic_allocator                         201902L <memory_resource>
 __cpp_lib_quoted_string_io                              201304L <iomanip>
-__cpp_lib_ranges                                        202106L <algorithm> <functional> <iterator>
+__cpp_lib_ranges                                        202207L <algorithm> <functional> <iterator>
                                                                 <memory> <ranges>
 __cpp_lib_ranges_as_rvalue                              202207L <ranges>
 __cpp_lib_ranges_chunk                                  202202L <ranges>
@@ -381,7 +381,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)
 #   define __cpp_lib_polymorphic_allocator              201902L
 # endif
-# define __cpp_lib_ranges                               202106L
+# define __cpp_lib_ranges                               202207L
 # define __cpp_lib_remove_cvref                         201711L
 # if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   define __cpp_lib_semaphore                          201907L

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp
deleted file mode 100644
index 0b9a050cf466b..0000000000000
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// Test various properties of <copyable-box>
-
-#include <ranges>
-
-#include <optional>
-
-#include "types.h"
-
-template <class T>
-constexpr bool valid_copyable_box = requires {
-  typename std::ranges::__copyable_box<T>;
-};
-
-struct NotCopyConstructible {
-  NotCopyConstructible() = default;
-  NotCopyConstructible(NotCopyConstructible&&) = default;
-  NotCopyConstructible(NotCopyConstructible const&) = delete;
-  NotCopyConstructible& operator=(NotCopyConstructible&&) = default;
-  NotCopyConstructible& operator=(NotCopyConstructible const&) = default;
-};
-
-static_assert(!valid_copyable_box<void>); // not an object type
-static_assert(!valid_copyable_box<int&>); // not an object type
-static_assert(!valid_copyable_box<NotCopyConstructible>);
-
-// primary template
-static_assert(sizeof(std::ranges::__copyable_box<CopyConstructible>) == sizeof(std::optional<CopyConstructible>));
-
-// optimization #1
-static_assert(sizeof(std::ranges::__copyable_box<Copyable>) == sizeof(Copyable));
-static_assert(alignof(std::ranges::__copyable_box<Copyable>) == alignof(Copyable));
-
-// optimization #2
-static_assert(sizeof(std::ranges::__copyable_box<NothrowCopyConstructible>) == sizeof(NothrowCopyConstructible));
-static_assert(alignof(std::ranges::__copyable_box<NothrowCopyConstructible>) == alignof(NothrowCopyConstructible));

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp
similarity index 82%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp
index e82840fbb98f6..a6764284e7f7e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp
@@ -19,11 +19,11 @@
 
 #include "types.h"
 
-template<class T>
+template <class T>
 constexpr void check() {
   // non-const version
   {
-    std::ranges::__copyable_box<T> x(std::in_place, 10);
+    std::ranges::__movable_box<T> x(std::in_place, 10);
     T* result = x.operator->();
     static_assert(noexcept(x.operator->()));
     assert(result->value == 10);
@@ -32,7 +32,7 @@ constexpr void check() {
 
   // const version
   {
-    std::ranges::__copyable_box<T> const x(std::in_place, 10);
+    std::ranges::__movable_box<T> const x(std::in_place, 10);
     const T* result = x.operator->();
     static_assert(noexcept(x.operator->()));
     assert(result->value == 10);
@@ -41,8 +41,8 @@ constexpr void check() {
 }
 
 constexpr bool test() {
-  check<CopyConstructible>(); // primary template
-  check<Copyable>(); // optimization #1
+  check<CopyConstructible>();        // primary template
+  check<Copyable>();                 // optimization #1
   check<NothrowCopyConstructible>(); // optimization #2
   return true;
 }

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp
similarity index 89%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp
index 0f6806b4b0a97..94c4dec2687b7 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp
@@ -24,8 +24,8 @@
 constexpr bool test() {
   // Test the primary template
   {
-    using Box = std::ranges::__copyable_box<CopyConstructible>;
-    static_assert( std::is_copy_assignable_v<Box>);
+    using Box = std::ranges::__movable_box<CopyConstructible>;
+    static_assert(std::is_copy_assignable_v<Box>);
     static_assert(!std::is_nothrow_copy_assignable_v<Box>);
 
     {
@@ -51,8 +51,8 @@ constexpr bool test() {
 
   // Test optimization #1 for copy-assignment
   {
-    using Box = std::ranges::__copyable_box<Copyable>;
-    static_assert( std::is_copy_assignable_v<Box>);
+    using Box = std::ranges::__movable_box<Copyable>;
+    static_assert(std::is_copy_assignable_v<Box>);
     static_assert(!std::is_nothrow_copy_assignable_v<Box>);
 
     {
@@ -80,7 +80,7 @@ constexpr bool test() {
 
   // Test optimization #2 for copy-assignment
   {
-    using Box = std::ranges::__copyable_box<NothrowCopyConstructible>;
+    using Box = std::ranges::__movable_box<NothrowCopyConstructible>;
     static_assert(std::is_copy_assignable_v<Box>);
     static_assert(std::is_nothrow_copy_assignable_v<Box>);
 
@@ -112,7 +112,7 @@ constexpr bool test() {
 // through throwing an exception.
 #if !defined(TEST_HAS_NO_EXCEPTIONS)
 void test_empty_state() {
-  using Box = std::ranges::__copyable_box<ThrowsOnCopy>;
+  using Box = std::ranges::__movable_box<ThrowsOnCopy>;
 
   // assign non-empty to empty
   {
@@ -137,7 +137,7 @@ void test_empty_state() {
   }
   // assign empty to empty
   {
-    Box x = create_empty_box();
+    Box x       = create_empty_box();
     Box const y = create_empty_box();
     Box& result = (x = y);
 
@@ -147,7 +147,7 @@ void test_empty_state() {
   }
   // check self-assignment in empty case
   {
-    Box x = create_empty_box();
+    Box x       = create_empty_box();
     Box& result = (x = x);
 
     assert(&result == &x);

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp
similarity index 84%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp
index bb46401178b30..d4dc612a8de63 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp
@@ -24,8 +24,8 @@
 constexpr bool test() {
   // Test the primary template
   {
-    using Box = std::ranges::__copyable_box<CopyConstructible>;
-    static_assert( std::is_move_assignable_v<Box>);
+    using Box = std::ranges::__movable_box<CopyConstructible>;
+    static_assert(std::is_move_assignable_v<Box>);
     static_assert(!std::is_nothrow_move_assignable_v<Box>);
 
     {
@@ -51,9 +51,10 @@ constexpr bool test() {
 
   // Make sure that we use the native move assignment in the primary template if we can.
   {
-    using Box = std::ranges::__copyable_box<CopyConstructibleMovable>;
+    using Box = std::ranges::__movable_box<CopyConstructibleMovable>;
     static_assert(std::is_move_assignable_v<Box>);
-    static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<CopyConstructibleMovable>);
+    static_assert(
+        std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<CopyConstructibleMovable>);
 
     {
       Box x(std::in_place, 5);
@@ -80,8 +81,8 @@ constexpr bool test() {
 
   // Test optimization #1 for move assignment
   {
-    using Box = std::ranges::__copyable_box<Copyable>;
-    static_assert( std::is_move_assignable_v<Box>);
+    using Box = std::ranges::__movable_box<Copyable>;
+    static_assert(std::is_move_assignable_v<Box>);
     static_assert(!std::is_nothrow_move_assignable_v<Box>);
 
     {
@@ -109,9 +110,10 @@ constexpr bool test() {
 
   // Test optimization #1 for move assignment with a type that uses optimization #2 for copy assignment
   {
-    using Box = std::ranges::__copyable_box<MovableNothrowCopyConstructible>;
+    using Box = std::ranges::__movable_box<MovableNothrowCopyConstructible>;
     static_assert(std::is_move_assignable_v<Box>);
-    static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<MovableNothrowCopyConstructible>);
+    static_assert(
+        std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<MovableNothrowCopyConstructible>);
 
     {
       Box x(std::in_place, 5);
@@ -138,7 +140,7 @@ constexpr bool test() {
 
   // Test optimization #2 for move assignment
   {
-    using Box = std::ranges::__copyable_box<NothrowCopyConstructible>;
+    using Box = std::ranges::__movable_box<NothrowCopyConstructible>;
     static_assert(std::is_move_assignable_v<Box>);
     static_assert(std::is_nothrow_move_assignable_v<Box>);
 
@@ -170,7 +172,7 @@ constexpr bool test() {
 // through throwing an exception.
 #if !defined(TEST_HAS_NO_EXCEPTIONS)
 void test_empty_state() {
-  using Box = std::ranges::__copyable_box<ThrowsOnCopy>;
+  using Box = std::ranges::__movable_box<ThrowsOnCopy>;
 
   // assign non-empty to empty
   {
@@ -186,7 +188,7 @@ void test_empty_state() {
   // assign empty to non-empty
   {
     Box x(std::in_place, 5);
-    Box y = create_empty_box();
+    Box y       = create_empty_box();
     Box& result = (x = std::move(y));
 
     assert(&result == &x);
@@ -195,8 +197,8 @@ void test_empty_state() {
   }
   // assign empty to empty
   {
-    Box x = create_empty_box();
-    Box y = create_empty_box();
+    Box x       = create_empty_box();
+    Box y       = create_empty_box();
     Box& result = (x = std::move(y));
 
     assert(&result == &x);
@@ -205,7 +207,7 @@ void test_empty_state() {
   }
   // check self-assignment in empty case
   {
-    Box x = create_empty_box();
+    Box x       = create_empty_box();
     Box& result = (x = std::move(x));
 
     assert(&result == &x);

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp
similarity index 89%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp
index c4d09576f13a0..3014dc00ca970 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp
@@ -18,19 +18,19 @@
 
 #include "types.h"
 
-template<class T>
-using Box = std::ranges::__copyable_box<T>;
+template <class T>
+using Box = std::ranges::__movable_box<T>;
 
 struct NoDefault {
   NoDefault() = delete;
 };
 static_assert(!std::is_default_constructible_v<Box<NoDefault>>);
 
-template<bool Noexcept>
+template <bool Noexcept>
 struct DefaultNoexcept {
   DefaultNoexcept() noexcept(Noexcept);
 };
-static_assert( std::is_nothrow_default_constructible_v<Box<DefaultNoexcept<true>>>);
+static_assert(std::is_nothrow_default_constructible_v<Box<DefaultNoexcept<true>>>);
 static_assert(!std::is_nothrow_default_constructible_v<Box<DefaultNoexcept<false>>>);
 
 constexpr bool test() {

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp
similarity index 73%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp
index 767d7cc69ada8..6f97b7eb0b9be 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "types.h"
 
-struct UnknownType { };
+struct UnknownType {};
 
-template<bool Noexcept>
+template <bool Noexcept>
 struct NothrowConstructible {
   explicit NothrowConstructible(int) noexcept(Noexcept);
 };
@@ -29,7 +29,7 @@ struct NothrowConstructible {
 constexpr bool test() {
   // Test the primary template
   {
-    using Box = std::ranges::__copyable_box<CopyConstructible>;
+    using Box = std::ranges::__movable_box<CopyConstructible>;
     Box x(std::in_place, 5);
     assert((*x).value == 5);
 
@@ -38,7 +38,7 @@ constexpr bool test() {
 
   // Test optimization #1
   {
-    using Box = std::ranges::__copyable_box<Copyable>;
+    using Box = std::ranges::__movable_box<Copyable>;
     Box x(std::in_place, 5);
     assert((*x).value == 5);
 
@@ -47,15 +47,17 @@ constexpr bool test() {
 
   // Test optimization #2
   {
-    using Box = std::ranges::__copyable_box<NothrowCopyConstructible>;
+    using Box = std::ranges::__movable_box<NothrowCopyConstructible>;
     Box x(std::in_place, 5);
     assert((*x).value == 5);
 
     static_assert(!std::is_constructible_v<Box, std::in_place_t, UnknownType>);
   }
 
-  static_assert( std::is_nothrow_constructible_v<std::ranges::__copyable_box<NothrowConstructible<true>>, std::in_place_t, int>);
-  static_assert(!std::is_nothrow_constructible_v<std::ranges::__copyable_box<NothrowConstructible<false>>, std::in_place_t, int>);
+  static_assert(
+      std::is_nothrow_constructible_v<std::ranges::__movable_box<NothrowConstructible<true>>, std::in_place_t, int>);
+  static_assert(
+      !std::is_nothrow_constructible_v<std::ranges::__movable_box<NothrowConstructible<false>>, std::in_place_t, int>);
 
   return true;
 }

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp
similarity index 80%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp
index 075d5783bef1b..37734f05a15b5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp
@@ -19,11 +19,11 @@
 
 #include "types.h"
 
-template<class T>
+template <class T>
 constexpr void check() {
   // non-const version
   {
-    std::ranges::__copyable_box<T> x(std::in_place, 10);
+    std::ranges::__movable_box<T> x(std::in_place, 10);
     T& result = *x;
     static_assert(noexcept(*x));
     assert(result.value == 10);
@@ -31,7 +31,7 @@ constexpr void check() {
 
   // const version
   {
-    std::ranges::__copyable_box<T> const x(std::in_place, 10);
+    std::ranges::__movable_box<T> const x(std::in_place, 10);
     T const& result = *x;
     static_assert(noexcept(*x));
     assert(result.value == 10);
@@ -39,8 +39,8 @@ constexpr void check() {
 }
 
 constexpr bool test() {
-  check<CopyConstructible>(); // primary template
-  check<Copyable>(); // optimization #1
+  check<CopyConstructible>();        // primary template
+  check<Copyable>();                 // optimization #1
   check<NothrowCopyConstructible>(); // optimization #2
   return true;
 }

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp
similarity index 79%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp
index aa68f23420749..397e6384b2afc 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp
@@ -18,15 +18,15 @@
 
 #include "types.h"
 
-template<class T>
+template <class T>
 constexpr void check() {
-  std::ranges::__copyable_box<T> const x(std::in_place, 10);
+  std::ranges::__movable_box<T> const x(std::in_place, 10);
   assert(x.__has_value());
 }
 
 constexpr bool test() {
-  check<CopyConstructible>(); // primary template
-  check<Copyable>(); // optimization #1
+  check<CopyConstructible>();        // primary template
+  check<Copyable>();                 // optimization #1
   check<NothrowCopyConstructible>(); // optimization #2
   return true;
 }
@@ -39,7 +39,7 @@ int main(int, char**) {
   // through throwing an exception.
 #if !defined(TEST_HAS_NO_EXCEPTIONS)
   {
-    std::ranges::__copyable_box<ThrowsOnCopy> x = create_empty_box();
+    std::ranges::__movable_box<ThrowsOnCopy> x = create_empty_box();
     assert(!x.__has_value());
   }
 #endif

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp
similarity index 86%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp
index 072527b8e80d3..e21191f98dea3 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp
@@ -16,19 +16,19 @@
 #include <utility>
 
 bool copied = false;
-bool moved = false;
+bool moved  = false;
 
 struct Empty {
-  Empty() noexcept { }
+  Empty() noexcept {}
   Empty(Empty const&) noexcept { copied = true; }
   Empty(Empty&&) noexcept { moved = true; }
   Empty& operator=(Empty const&) = delete;
-  Empty& operator=(Empty&&) = delete;
+  Empty& operator=(Empty&&)      = delete;
 };
 
-using Box = std::ranges::__copyable_box<Empty>;
+using Box = std::ranges::__movable_box<Empty>;
 
-struct Inherit : Box { };
+struct Inherit : Box {};
 
 struct Hold : Box {
   [[no_unique_address]] Inherit member;
@@ -37,7 +37,7 @@ struct Hold : Box {
 int main(int, char**) {
   Hold box;
 
-  Box& base = static_cast<Box&>(box);
+  Box& base   = static_cast<Box&>(box);
   Box& member = static_cast<Box&>(box.member);
 
   // Despite [[no_unique_address]], the two objects have the same type so they

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp
new file mode 100644
index 0000000000000..6596c7074117f
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Test various properties of <copyable-box>
+
+#include <ranges>
+
+#include <optional>
+
+#include "MoveOnly.h"
+
+#include "types.h"
+
+template <class T>
+constexpr bool valid_movable_box = requires { typename std::ranges::__movable_box<T>; };
+
+struct NotCopyConstructible {
+  NotCopyConstructible()                                       = default;
+  NotCopyConstructible(NotCopyConstructible&&)                 = default;
+  NotCopyConstructible(NotCopyConstructible const&)            = delete;
+  NotCopyConstructible& operator=(NotCopyConstructible&&)      = default;
+  NotCopyConstructible& operator=(NotCopyConstructible const&) = default;
+};
+
+static_assert(!valid_movable_box<void>); // not an object type
+static_assert(!valid_movable_box<int&>); // not an object type
+
+#if _LIBCPP_STD_VER >= 23
+struct NotCopyConstructibleNotMoveConstructible {
+  NotCopyConstructibleNotMoveConstructible()                                                           = default;
+  NotCopyConstructibleNotMoveConstructible(NotCopyConstructibleNotMoveConstructible&&)                 = delete;
+  NotCopyConstructibleNotMoveConstructible(NotCopyConstructibleNotMoveConstructible const&)            = delete;
+  NotCopyConstructibleNotMoveConstructible& operator=(NotCopyConstructibleNotMoveConstructible&&)      = delete;
+  NotCopyConstructibleNotMoveConstructible& operator=(NotCopyConstructibleNotMoveConstructible const&) = delete;
+};
+
+// [P2494R2] Relaxing range adaptors to allow for move only types.
+static_assert(!valid_movable_box<NotCopyConstructibleNotMoveConstructible>);
+static_assert(valid_movable_box<NotCopyConstructible>);
+static_assert(valid_movable_box<MoveOnly>);
+#else
+static_assert(!valid_movable_box<NotCopyConstructible>);
+#endif
+
+// primary template
+static_assert(sizeof(std::ranges::__movable_box<CopyConstructible>) == sizeof(std::optional<CopyConstructible>));
+
+// optimization #1
+static_assert(sizeof(std::ranges::__movable_box<Copyable>) == sizeof(Copyable));
+static_assert(alignof(std::ranges::__movable_box<Copyable>) == alignof(Copyable));
+
+// optimization #2
+static_assert(sizeof(std::ranges::__movable_box<NothrowCopyConstructible>) == sizeof(NothrowCopyConstructible));
+static_assert(alignof(std::ranges::__movable_box<NothrowCopyConstructible>) == alignof(NothrowCopyConstructible));

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h
similarity index 79%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h
rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h
index bcc3493679444..9e6bf5d89c18e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h
@@ -16,15 +16,15 @@
 
 #include "test_macros.h"
 
-// NOTE: These types are strongly tied to the implementation of __copyable_box. See the documentation
-//       in __copyable_box for the meaning of optimizations #1 and #2.
+// NOTE: These types are strongly tied to the implementation of __movable_box. See the documentation
+//       in __movable_box for the meaning of optimizations #1 and #2.
 
 // Copy constructible, but neither copyable nor nothrow_copy/move_constructible. This uses the primary template.
 struct CopyConstructible {
   constexpr CopyConstructible() = default;
-  constexpr explicit CopyConstructible(int x) : value(x) { }
+  constexpr explicit CopyConstructible(int x) : value(x) {}
   CopyConstructible(CopyConstructible const&) noexcept(false) = default;
-  CopyConstructible& operator=(CopyConstructible const&) = delete;
+  CopyConstructible& operator=(CopyConstructible const&)      = delete;
 
   int value = -1;
 };
@@ -33,105 +33,102 @@ static_assert(!std::is_nothrow_copy_constructible_v<CopyConstructible>);
 static_assert(!std::movable<CopyConstructible>);
 static_assert(!std::is_nothrow_move_constructible_v<CopyConstructible>);
 
-
 // Copy constructible and movable, but not copyable. This uses the primary template, however we're
 // still able to use the native move-assignment operator in this case.
 struct CopyConstructibleMovable {
   constexpr CopyConstructibleMovable() = default;
-  constexpr explicit CopyConstructibleMovable(int x) : value(x) { }
+  constexpr explicit CopyConstructibleMovable(int x) : value(x) {}
   CopyConstructibleMovable(CopyConstructibleMovable const&) noexcept(false) = default;
-  CopyConstructibleMovable(CopyConstructibleMovable&&) noexcept(false) = default;
-  CopyConstructibleMovable& operator=(CopyConstructibleMovable const&) = delete;
+  CopyConstructibleMovable(CopyConstructibleMovable&&) noexcept(false)      = default;
+  CopyConstructibleMovable& operator=(CopyConstructibleMovable const&)      = delete;
 
   constexpr CopyConstructibleMovable& operator=(CopyConstructibleMovable&& other) {
-    value = other.value;
+    value           = other.value;
     did_move_assign = true;
     return *this;
   }
 
-  int value = -1;
+  int value            = -1;
   bool did_move_assign = false;
 };
 
-
 // Copyable type that is not nothrow_copy/move_constructible.
 // This triggers optimization #1 for the copy assignment and the move assignment.
 struct Copyable {
   constexpr Copyable() = default;
-  constexpr explicit Copyable(int x) : value(x) { }
+  constexpr explicit Copyable(int x) : value(x) {}
   Copyable(Copyable const&) noexcept(false) = default;
 
   constexpr Copyable& operator=(Copyable const& other) noexcept(false) {
-    value = other.value;
+    value           = other.value;
     did_copy_assign = true;
     return *this;
   }
 
   constexpr Copyable& operator=(Copyable&& other) noexcept(false) {
-    value = other.value;
+    value           = other.value;
     did_move_assign = true;
     return *this;
   }
 
-  int value = -1;
+  int value            = -1;
   bool did_copy_assign = false;
   bool did_move_assign = false;
 };
-static_assert( std::copyable<Copyable>);
+static_assert(std::copyable<Copyable>);
 static_assert(!std::is_nothrow_copy_constructible_v<Copyable>);
-static_assert( std::movable<Copyable>);
+static_assert(std::movable<Copyable>);
 static_assert(!std::is_nothrow_move_constructible_v<Copyable>);
 
-
 // Non-copyable type that is nothrow_copy_constructible and nothrow_move_constructible.
 // This triggers optimization #2 for the copy assignment and the move assignment.
 struct NothrowCopyConstructible {
   constexpr NothrowCopyConstructible() = default;
-  constexpr explicit NothrowCopyConstructible(int x) : value(x) { }
-  NothrowCopyConstructible(NothrowCopyConstructible const&) noexcept = default;
-  NothrowCopyConstructible(NothrowCopyConstructible&&) noexcept = default;
+  constexpr explicit NothrowCopyConstructible(int x) : value(x) {}
+  NothrowCopyConstructible(NothrowCopyConstructible const&) noexcept   = default;
+  NothrowCopyConstructible(NothrowCopyConstructible&&) noexcept        = default;
   NothrowCopyConstructible& operator=(NothrowCopyConstructible const&) = delete;
 
   int value = -1;
 };
 static_assert(!std::copyable<NothrowCopyConstructible>);
-static_assert( std::is_nothrow_copy_constructible_v<NothrowCopyConstructible>);
+static_assert(std::is_nothrow_copy_constructible_v<NothrowCopyConstructible>);
 static_assert(!std::movable<NothrowCopyConstructible>);
-static_assert( std::is_nothrow_move_constructible_v<NothrowCopyConstructible>);
-
+static_assert(std::is_nothrow_move_constructible_v<NothrowCopyConstructible>);
 
 // Non-copyable type that is nothrow_copy_constructible, and that is movable but NOT nothrow_move_constructible.
 // This triggers optimization #2 for the copy assignment, and optimization #1 for the move assignment.
 struct MovableNothrowCopyConstructible {
   constexpr MovableNothrowCopyConstructible() = default;
-  constexpr explicit MovableNothrowCopyConstructible(int x) : value(x) { }
-  MovableNothrowCopyConstructible(MovableNothrowCopyConstructible const&) noexcept = default;
+  constexpr explicit MovableNothrowCopyConstructible(int x) : value(x) {}
+  MovableNothrowCopyConstructible(MovableNothrowCopyConstructible const&) noexcept   = default;
   MovableNothrowCopyConstructible(MovableNothrowCopyConstructible&&) noexcept(false) = default;
   constexpr MovableNothrowCopyConstructible& operator=(MovableNothrowCopyConstructible&& other) {
-    value = other.value;
+    value           = other.value;
     did_move_assign = true;
     return *this;
   }
 
-  int value = -1;
+  int value            = -1;
   bool did_move_assign = false;
 };
 static_assert(!std::copyable<MovableNothrowCopyConstructible>);
-static_assert( std::is_nothrow_copy_constructible_v<MovableNothrowCopyConstructible>);
-static_assert( std::movable<MovableNothrowCopyConstructible>);
+static_assert(std::is_nothrow_copy_constructible_v<MovableNothrowCopyConstructible>);
+static_assert(std::movable<MovableNothrowCopyConstructible>);
 static_assert(!std::is_nothrow_move_constructible_v<MovableNothrowCopyConstructible>);
 
-
 #if !defined(TEST_HAS_NO_EXCEPTIONS)
 // A type that we can make throw when copied from. This is used to create a
 // copyable-box in the empty state.
 static constexpr int THROW_WHEN_COPIED_FROM = 999;
 struct ThrowsOnCopy {
   constexpr ThrowsOnCopy() = default;
-  constexpr explicit ThrowsOnCopy(int x) : value(x) { }
+  constexpr explicit ThrowsOnCopy(int x) : value(x) {}
   ThrowsOnCopy(ThrowsOnCopy const& other) {
-    if (other.value == THROW_WHEN_COPIED_FROM) throw 0;
-    else                                       value = other.value;
+    if (other.value == THROW_WHEN_COPIED_FROM)
+      throw 0;
+    else
+      value = other.value;
   }
 
   ThrowsOnCopy& operator=(ThrowsOnCopy const&) = delete; // prevent from being copyable
@@ -142,9 +139,9 @@ struct ThrowsOnCopy {
 // Creates an empty box. The only way to do that is to try assigning one box
 // to another and have that fail due to an exception when calling the copy
 // constructor. The assigned-to box will then be in the empty state.
-inline std::ranges::__copyable_box<ThrowsOnCopy> create_empty_box() {
-  std::ranges::__copyable_box<ThrowsOnCopy> box1;
-  std::ranges::__copyable_box<ThrowsOnCopy> box2(std::in_place, THROW_WHEN_COPIED_FROM);
+inline std::ranges::__movable_box<ThrowsOnCopy> create_empty_box() {
+  std::ranges::__movable_box<ThrowsOnCopy> box1;
+  std::ranges::__movable_box<ThrowsOnCopy> box2(std::in_place, THROW_WHEN_COPIED_FROM);
   try {
     box1 = box2; // throws during assignment, which is implemented as a call to the copy ctor
   } catch (...) {

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
index d93c636150ad9..1cd58f081952f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
@@ -19,7 +19,7 @@
     __cpp_lib_clamp                          201603L [C++17]
     __cpp_lib_constexpr_algorithms           201806L [C++20]
     __cpp_lib_parallel_algorithm             201603L [C++17]
-    __cpp_lib_ranges                         202106L [C++20]
+    __cpp_lib_ranges                         202207L [C++20]
     __cpp_lib_ranges_starts_ends_with        202106L [C++23]
     __cpp_lib_robust_nonmodifying_seq_ops    201304L [C++14]
     __cpp_lib_sample                         201603L [C++17]
@@ -184,8 +184,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifdef __cpp_lib_ranges_starts_ends_with
@@ -245,8 +245,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # if !defined(_LIBCPP_VERSION)
@@ -315,8 +315,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # if !defined(_LIBCPP_VERSION)

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index f14b127e51951..72c96c62b64c4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -28,7 +28,7 @@
     __cpp_lib_invoke_r                 202106L [C++23]
     __cpp_lib_move_only_function       202110L [C++23]
     __cpp_lib_not_fn                   201603L [C++17]
-    __cpp_lib_ranges                   202106L [C++20]
+    __cpp_lib_ranges                   202207L [C++20]
     __cpp_lib_result_of_sfinae         201210L [C++14]
     __cpp_lib_transparent_operators    201210L [C++14]
                                        201510L [C++17]
@@ -293,8 +293,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifndef __cpp_lib_result_of_sfinae
@@ -399,8 +399,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_result_of_sfinae
@@ -523,8 +523,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_result_of_sfinae

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
index 2a292dc8d7d79..700907ce9bb07 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
@@ -23,7 +23,7 @@
     __cpp_lib_move_iterator_concept         202207L [C++20]
     __cpp_lib_nonmember_container_access    201411L [C++17]
     __cpp_lib_null_iterators                201304L [C++14]
-    __cpp_lib_ranges                        202106L [C++20]
+    __cpp_lib_ranges                        202207L [C++20]
     __cpp_lib_ssize                         201902L [C++20]
 */
 
@@ -197,8 +197,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifndef __cpp_lib_ssize
@@ -255,8 +255,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_ssize
@@ -313,8 +313,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_ssize

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index 02559d1340531..94cb49750b340 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -27,7 +27,7 @@
     __cpp_lib_enable_shared_from_this             201603L [C++17]
     __cpp_lib_make_unique                         201304L [C++14]
     __cpp_lib_out_ptr                             202106L [C++23]
-    __cpp_lib_ranges                              202106L [C++20]
+    __cpp_lib_ranges                              202207L [C++20]
     __cpp_lib_raw_memory_algorithms               201606L [C++17]
     __cpp_lib_shared_ptr_arrays                   201611L [C++17]
                                                   201707L [C++20]
@@ -363,8 +363,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifndef __cpp_lib_raw_memory_algorithms
@@ -500,8 +500,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_raw_memory_algorithms
@@ -637,8 +637,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_raw_memory_algorithms

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 88e059dcacff9..7a1edd1798eeb 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -16,7 +16,7 @@
 // Test the feature test macros defined by <ranges>
 
 /*  Constant                      Value
-    __cpp_lib_ranges              202106L [C++20]
+    __cpp_lib_ranges              202207L [C++20]
     __cpp_lib_ranges_as_rvalue    202207L [C++23]
     __cpp_lib_ranges_chunk        202202L [C++23]
     __cpp_lib_ranges_chunk_by     202202L [C++23]
@@ -123,8 +123,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifdef __cpp_lib_ranges_as_rvalue
@@ -156,8 +156,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
@@ -237,8 +237,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 06301620781f7..e969d22af55d4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -139,7 +139,7 @@
     __cpp_lib_parallel_algorithm                     201603L [C++17]
     __cpp_lib_polymorphic_allocator                  201902L [C++20]
     __cpp_lib_quoted_string_io                       201304L [C++14]
-    __cpp_lib_ranges                                 202106L [C++20]
+    __cpp_lib_ranges                                 202207L [C++20]
     __cpp_lib_ranges_as_rvalue                       202207L [C++23]
     __cpp_lib_ranges_chunk                           202202L [C++23]
     __cpp_lib_ranges_chunk_by                        202202L [C++23]
@@ -3540,8 +3540,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++20"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++20"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++20"
 # endif
 
 # ifdef __cpp_lib_ranges_as_rvalue
@@ -4899,8 +4899,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++23"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++23"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
@@ -6438,8 +6438,8 @@
 # ifndef __cpp_lib_ranges
 #   error "__cpp_lib_ranges should be defined in c++26"
 # endif
-# if __cpp_lib_ranges != 202106L
-#   error "__cpp_lib_ranges should have the value 202106L in c++26"
+# if __cpp_lib_ranges != 202207L
+#   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
index f48aae6d2887d..32fed6f8caa7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include <cassert>
+#include "MoveOnly.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "types.h"
@@ -47,6 +48,15 @@ auto joinArrays(E1 (&a)[N], E2 (&b)[N], Join join = Join()) {
   });
 }
 
+#if _LIBCPP_STD_VER >= 23
+struct MoveOnlyFunction : public MoveOnly {
+  template <class T>
+  constexpr T operator()(T x) const {
+    return x + 42;
+  }
+};
+#endif
+
 struct NonConstView : std::ranges::view_base {
   explicit NonConstView(int *b, int *e) : b_(b), e_(e) {}
   const int *begin() { return b_; }  // deliberately non-const
@@ -87,6 +97,16 @@ int main(int, char**) {
     std::string_view check = "HELLO, WORLD.";
     assert(std::equal(upp.begin(), upp.end(), check.begin(), check.end()));
   }
+#if _LIBCPP_STD_VER >= 23
+  // [P2494R2] Relaxing range adaptors to allow for move only types.
+  // Test transform_view is valid when the function object is a move only type.
+  {
+    int a[]          = {1, 2, 3, 4};
+    auto transformed = NonConstView(a, a + 4) | std::views::transform(MoveOnlyFunction());
+    int expected[]   = {43, 44, 45, 46};
+    assert(std::equal(transformed.begin(), transformed.end(), expected, expected + 4));
+  }
+#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp b/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp
index d0cd2393423b1..d818a749c68cb 100644
--- a/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp
@@ -19,9 +19,10 @@
 
 // Can't invoke without arguments.
 static_assert(!std::is_invocable_v<decltype((std::views::single))>);
-// Can't invoke with a move-only type.
-static_assert(!std::is_invocable_v<decltype((std::views::single)), MoveOnly>);
-
+#if _LIBCPP_STD_VER >= 23
+// Can invoke with a move-only type.
+static_assert(std::is_invocable_v<decltype((std::views::single)), MoveOnly>);
+#endif
 constexpr bool test() {
   // Lvalue.
   {

diff  --git a/libcxx/utils/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt
index f43f1ccb0cf34..c9951198b0987 100644
--- a/libcxx/utils/data/ignore_format.txt
+++ b/libcxx/utils/data/ignore_format.txt
@@ -413,7 +413,6 @@ libcxx/include/__ranges/access.h
 libcxx/include/__ranges/all.h
 libcxx/include/__ranges/common_view.h
 libcxx/include/__ranges/concepts.h
-libcxx/include/__ranges/copyable_box.h
 libcxx/include/__ranges/counted.h
 libcxx/include/__ranges/data.h
 libcxx/include/__ranges/drop_view.h

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index c30fb334dd0b4..3f4ba0e743761 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -778,7 +778,7 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_ranges",
-            "values": {"c++20": 202106},
+            "values": {"c++20": 202207},
             "headers": ["algorithm", "functional", "iterator", "memory", "ranges"],
         },
         {

diff  --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
index 068f87d87abc7..3b4bd9e8858e9 100644
--- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
@@ -680,7 +680,7 @@ if (current_toolchain == default_toolchain) {
       "__ranges/common_view.h",
       "__ranges/concepts.h",
       "__ranges/container_compatible_range.h",
-      "__ranges/copyable_box.h",
+      "__ranges/movable_box.h",
       "__ranges/counted.h",
       "__ranges/dangling.h",
       "__ranges/data.h",


        


More information about the llvm-commits mailing list