[libc++][ranges] Implement P2494R2 (Relaxing range adaptors to allow for move only...
authoryronglin <yronglin777@gmail.com>
Sun, 25 Jun 2023 00:14:55 +0000 (08:14 +0800)
committeryronglin <yronglin777@gmail.com>
Sun, 25 Jun 2023 00:15:52 +0000 (08:15 +0800)
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

35 files changed:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/Status/Cxx23Papers.csv
libcxx/include/CMakeLists.txt
libcxx/include/__ranges/copyable_box.h [deleted file]
libcxx/include/__ranges/drop_while_view.h
libcxx/include/__ranges/filter_view.h
libcxx/include/__ranges/iota_view.h
libcxx/include/__ranges/movable_box.h [new file with mode: 0644]
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/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp [deleted file]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp with 82% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp with 89% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp with 84% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp with 89% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp with 73% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp with 80% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp with 79% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp with 86% similarity]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h [moved from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h with 79% similarity]
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

index 9fa4495..deb3b3d 100644 (file)
@@ -264,7 +264,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_polymorphic_allocator``                 ``201902L``
     --------------------------------------------------- -----------------
-    ``__cpp_lib_ranges``                                ``202106L``
+    ``__cpp_lib_ranges``                                ``202207L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_remove_cvref``                          ``201711L``
     --------------------------------------------------- -----------------
index c38327b..034ea99 100644 (file)
@@ -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"
index a8e4011..7de36c0 100644 (file)
@@ -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 (file)
index 9fbbb1e..0000000
+++ /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
index 518feae..2f5fdb5 100644 (file)
@@ -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 @@ public:
 
 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>;
index 4acbfad..77c3553 100644 (file)
@@ -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.
index ebfa022..a08fde2 100644 (file)
@@ -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 (file)
index 0000000..8b3716a
--- /dev/null
@@ -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
index ccab2c1..5724e2d 100644 (file)
@@ -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>;
index d1f1bfe..b4bdd18 100644 (file)
@@ -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()
index 8bdfa97..dd346fb 100644 (file)
@@ -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 @@ public:
   }
 };
 
-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>;
index cdfb0e9..d49e339 100644 (file)
@@ -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" }
index ac93206..4091a49 100644 (file)
@@ -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 (file)
index 0b9a050..0000000
+++ /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));
 
 #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;
 }
@@ -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);
@@ -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);
 
 #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() {
@@ -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;
 }
 
 #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;
 }
 
 #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
 #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 (file)
index 0000000..6596c70
--- /dev/null
@@ -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));
 
 #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 (...) {
index d93c636..1cd58f0 100644 (file)
@@ -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]
 # 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
 # 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)
 # 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)
index f14b127..72c96c6 100644 (file)
@@ -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]
 # 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
 # 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
 # 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
index 2a292dc..700907c 100644 (file)
@@ -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]
 */
 
 # 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
 # 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
 # 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
index 02559d1..94cb497 100644 (file)
@@ -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]
 # 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
 # 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
 # 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
index 88e059d..7a1edd1 100644 (file)
@@ -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]
 # 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
 # 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
 # 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
index 0630162..e969d22 100644 (file)
     __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]
 # 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
 # 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
 # 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
index f48aae6..32fed6f 100644 (file)
@@ -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;
 }
index d0cd239..d818a74 100644 (file)
 
 // 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.
   {
index f43f1cc..c995119 100644 (file)
@@ -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
index c30fb33..3f4ba0e 100755 (executable)
@@ -778,7 +778,7 @@ feature_test_macros = [
         },
         {
             "name": "__cpp_lib_ranges",
-            "values": {"c++20": 202106},
+            "values": {"c++20": 202207},
             "headers": ["algorithm", "functional", "iterator", "memory", "ranges"],
         },
         {
index 068f87d..3b4bd9e 100644 (file)
@@ -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",