diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -264,7 +264,7 @@ ------------------------------------------------- ----------------- ``__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 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -78,7 +78,7 @@ "`P2465R3 `__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","","" "`P2467R1 `__","LWG","Support exclusive mode for ``fstreams``","July 2022","","" "`P2474R2 `__","LWG","``views::repeat``","July 2022","","","|ranges|" -"`P2494R2 `__","LWG","Relaxing range adaptors to allow for move only types","July 2022","","","|ranges|" +"`P2494R2 `__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|" "`P2499R0 `__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|" "`P2502R2 `__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","","","|ranges|" "`P2508R1 `__","LWG","Exposing ``std::basic-format-string``","July 2022","|Complete|","15.0" diff --git a/libcxx/include/__ranges/copyable_box.h b/libcxx/include/__ranges/copyable_box.h --- a/libcxx/include/__ranges/copyable_box.h +++ b/libcxx/include/__ranges/copyable_box.h @@ -30,44 +30,53 @@ #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 +// __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 -// __copyable_box that does this, see below for the details. +// __movable_box that does this, see below for the details. +// Relaxing range adaptors to allow for move only types since C++23. template -concept __copy_constructible_object = copy_constructible<_Tp> && is_object_v<_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<__copy_constructible_object _Tp> - class __copyable_box { + template<__movable_box_object _Tp> + class __movable_box { _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_; public: template requires is_constructible_v<_Tp, _Args...> _LIBCPP_HIDE_FROM_ABI - constexpr explicit __copyable_box(in_place_t, _Args&& ...__args) + 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 __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) + constexpr __movable_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 __movable_box(__movable_box const&) = default; + _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; - _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box& operator=(__copyable_box const& __other) - noexcept(is_nothrow_copy_constructible_v<_Tp>) + _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); @@ -77,10 +86,10 @@ } _LIBCPP_HIDE_FROM_ABI - __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default; + __movable_box& operator=(__movable_box&&) requires movable<_Tp> = default; _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box& operator=(__copyable_box&& __other) + constexpr __movable_box& operator=(__movable_box&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) { if (this != std::addressof(__other)) { @@ -111,42 +120,60 @@ // 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 + concept __doesnt_need_empty_state = + (copy_constructible<_Tp> + // 1. If copy_constructible is true, movable-box should store only a T if either T models + // copyable, or is_nothrow_move_constructible_v && is_nothrow_copy_constructible_v is true. + ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>) + // 2. Otherwise, movable-box should store only a T if either T models movable or + // is_nothrow_move_constructible_v is true. + : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>); +#else + template concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>; template 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> { + template + 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 requires is_constructible_v<_Tp, _Args...> _LIBCPP_HIDE_FROM_ABI - constexpr explicit __copyable_box(in_place_t, _Args&& ...__args) + 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 __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) + constexpr __movable_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; + _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 __copyable_box& operator=(__copyable_box const&) requires copyable<_Tp> = default; - _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default; + _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 __copyable_box& operator=(__copyable_box const& __other) noexcept { + 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_)); @@ -156,7 +183,7 @@ } _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box& operator=(__copyable_box&& __other) noexcept { + 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_)); diff --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h --- a/libcxx/include/__ranges/drop_while_view.h +++ b/libcxx/include/__ranges/drop_while_view.h @@ -82,7 +82,7 @@ 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>, __empty_cache>; diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h --- a/libcxx/include/__ranges/filter_view.h +++ b/libcxx/include/__ranges/filter_view.h @@ -53,7 +53,7 @@ requires view<_View> && is_object_v<_Pred> class filter_view : public view_interface> { _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/single_view.h b/libcxx/include/__ranges/single_view.h --- a/libcxx/include/__ranges/single_view.h +++ b/libcxx/include/__ranges/single_view.h @@ -31,17 +31,24 @@ #if _LIBCPP_STD_VER >= 20 namespace ranges { +#if _LIBCPP_STD_VER >= 23 + template +#else template +#endif requires is_object_v<_Tp> class single_view : public view_interface> { - __copyable_box<_Tp> __value_; + __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) : __value_(in_place, __t) {} + _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)) {} diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h --- a/libcxx/include/__ranges/take_while_view.h +++ b/libcxx/include/__ranges/take_while_view.h @@ -60,7 +60,7 @@ 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 --- a/libcxx/include/__ranges/transform_view.h +++ b/libcxx/include/__ranges/transform_view.h @@ -62,13 +62,17 @@ regular_invocable<_Fn&, range_reference_t<_View>> && __can_reference>>; +#if _LIBCPP_STD_VER >= 23 +template +#else template +#endif requires __transform_view_constraints<_View, _Fn> class transform_view : public view_interface> { template class __iterator; template 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,7 +165,11 @@ >; }; +#if _LIBCPP_STD_VER >= 23 +template +#else template +#endif requires __transform_view_constraints<_View, _Fn> template class transform_view<_View, _Fn>::__iterator @@ -357,7 +365,11 @@ } }; +#if _LIBCPP_STD_VER >= 23 +template +#else template +#endif requires __transform_view_constraints<_View, _Fn> template class transform_view<_View, _Fn>::__sentinel { diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -141,7 +141,7 @@ __cpp_lib_parallel_algorithm 201603L __cpp_lib_polymorphic_allocator 201902L __cpp_lib_quoted_string_io 201304L -__cpp_lib_ranges 202106L +__cpp_lib_ranges 202207L __cpp_lib_ranges_as_rvalue 202207L __cpp_lib_ranges_chunk 202202L @@ -360,7 +360,7 @@ # define __cpp_lib_math_constants 201907L # define __cpp_lib_move_iterator_concept 202207L # define __cpp_lib_polymorphic_allocator 201902L -# 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/arrow.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp @@ -23,7 +23,7 @@ constexpr void check() { // non-const version { - std::ranges::__copyable_box x(std::in_place, 10); + std::ranges::__movable_box x(std::in_place, 10); T* result = x.operator->(); static_assert(noexcept(x.operator->())); assert(result->value == 10); @@ -32,7 +32,7 @@ // const version { - std::ranges::__copyable_box const x(std::in_place, 10); + std::ranges::__movable_box const x(std::in_place, 10); const T* result = x.operator->(); static_assert(noexcept(x.operator->())); assert(result->value == 10); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp @@ -24,7 +24,7 @@ constexpr bool test() { // Test the primary template { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert( std::is_copy_assignable_v); static_assert(!std::is_nothrow_copy_assignable_v); @@ -51,7 +51,7 @@ // Test optimization #1 for copy-assignment { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert( std::is_copy_assignable_v); static_assert(!std::is_nothrow_copy_assignable_v); @@ -80,7 +80,7 @@ // Test optimization #2 for copy-assignment { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert(std::is_copy_assignable_v); static_assert(std::is_nothrow_copy_assignable_v); @@ -112,7 +112,7 @@ // through throwing an exception. #if !defined(TEST_HAS_NO_EXCEPTIONS) void test_empty_state() { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; // assign non-empty to empty { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp @@ -24,7 +24,7 @@ constexpr bool test() { // Test the primary template { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert( std::is_move_assignable_v); static_assert(!std::is_nothrow_move_assignable_v); @@ -51,7 +51,7 @@ // Make sure that we use the native move assignment in the primary template if we can. { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert(std::is_move_assignable_v); static_assert(std::is_nothrow_move_assignable_v == std::is_nothrow_move_assignable_v); @@ -80,7 +80,7 @@ // Test optimization #1 for move assignment { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert( std::is_move_assignable_v); static_assert(!std::is_nothrow_move_assignable_v); @@ -109,7 +109,7 @@ // Test optimization #1 for move assignment with a type that uses optimization #2 for copy assignment { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert(std::is_move_assignable_v); static_assert(std::is_nothrow_move_assignable_v == std::is_nothrow_move_assignable_v); @@ -138,7 +138,7 @@ // Test optimization #2 for move assignment { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; static_assert(std::is_move_assignable_v); static_assert(std::is_nothrow_move_assignable_v); @@ -170,7 +170,7 @@ // through throwing an exception. #if !defined(TEST_HAS_NO_EXCEPTIONS) void test_empty_state() { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; // assign non-empty to empty { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp @@ -19,7 +19,7 @@ #include "types.h" template -using Box = std::ranges::__copyable_box; +using Box = std::ranges::__movable_box; struct NoDefault { NoDefault() = delete; 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.copy.wrap/ctor.in_place.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp @@ -29,7 +29,7 @@ constexpr bool test() { // Test the primary template { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; Box x(std::in_place, 5); assert((*x).value == 5); @@ -38,7 +38,7 @@ // Test optimization #1 { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; Box x(std::in_place, 5); assert((*x).value == 5); @@ -47,15 +47,15 @@ // Test optimization #2 { - using Box = std::ranges::__copyable_box; + using Box = std::ranges::__movable_box; Box x(std::in_place, 5); assert((*x).value == 5); static_assert(!std::is_constructible_v); } - static_assert( std::is_nothrow_constructible_v>, std::in_place_t, int>); - static_assert(!std::is_nothrow_constructible_v>, std::in_place_t, int>); + static_assert( std::is_nothrow_constructible_v>, std::in_place_t, int>); + static_assert(!std::is_nothrow_constructible_v>, 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.copy.wrap/deref.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp @@ -23,7 +23,7 @@ constexpr void check() { // non-const version { - std::ranges::__copyable_box x(std::in_place, 10); + std::ranges::__movable_box x(std::in_place, 10); T& result = *x; static_assert(noexcept(*x)); assert(result.value == 10); @@ -31,7 +31,7 @@ // const version { - std::ranges::__copyable_box const x(std::in_place, 10); + std::ranges::__movable_box const x(std::in_place, 10); T const& result = *x; static_assert(noexcept(*x)); assert(result.value == 10); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp @@ -20,7 +20,7 @@ template constexpr void check() { - std::ranges::__copyable_box const x(std::in_place, 10); + std::ranges::__movable_box const x(std::in_place, 10); assert(x.__has_value()); } @@ -39,7 +39,7 @@ // through throwing an exception. #if !defined(TEST_HAS_NO_EXCEPTIONS) { - std::ranges::__copyable_box x = create_empty_box(); + std::ranges::__movable_box 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.copy.wrap/no_unique_address.pass.cpp --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp @@ -26,7 +26,7 @@ Empty& operator=(Empty&&) = delete; }; -using Box = std::ranges::__copyable_box; +using Box = std::ranges::__movable_box; struct Inherit : Box { }; 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 --- 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 @@ -14,11 +14,13 @@ #include +#include "MoveOnly.h" + #include "types.h" template -constexpr bool valid_copyable_box = requires { - typename std::ranges::__copyable_box; +constexpr bool valid_movable_box = requires { + typename std::ranges::__movable_box; }; struct NotCopyConstructible { @@ -29,17 +31,33 @@ NotCopyConstructible& operator=(NotCopyConstructible const&) = default; }; -static_assert(!valid_copyable_box); // not an object type -static_assert(!valid_copyable_box); // not an object type -static_assert(!valid_copyable_box); +static_assert(!valid_movable_box); // not an object type +static_assert(!valid_movable_box); // 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); +static_assert(valid_movable_box); +static_assert(valid_movable_box); +#else +static_assert(!valid_movable_box); +#endif // primary template -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(std::optional)); +static_assert(sizeof(std::ranges::__movable_box) == sizeof(std::optional)); // optimization #1 -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(Copyable)); -static_assert(alignof(std::ranges::__copyable_box) == alignof(Copyable)); +static_assert(sizeof(std::ranges::__movable_box) == sizeof(Copyable)); +static_assert(alignof(std::ranges::__movable_box) == alignof(Copyable)); // optimization #2 -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(NothrowCopyConstructible)); -static_assert(alignof(std::ranges::__copyable_box) == alignof(NothrowCopyConstructible)); +static_assert(sizeof(std::ranges::__movable_box) == sizeof(NothrowCopyConstructible)); +static_assert(alignof(std::ranges::__movable_box) == alignof(NothrowCopyConstructible)); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h @@ -16,8 +16,8 @@ #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 { @@ -142,9 +142,9 @@ // 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 create_empty_box() { - std::ranges::__copyable_box box1; - std::ranges::__copyable_box box2(std::in_place, THROW_WHEN_COPIED_FROM); +inline std::ranges::__movable_box create_empty_box() { + std::ranges::__movable_box box1; + std::ranges::__movable_box 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 --- 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 --- 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 @@ -24,7 +24,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] @@ -257,8 +257,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 @@ -355,8 +355,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 @@ -453,8 +453,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 --- 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 --- 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] @@ -350,8 +350,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 @@ -483,8 +483,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 @@ -616,8 +616,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 --- 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 /* 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 --- 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 @@ -131,7 +131,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] @@ -3320,8 +3320,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 @@ -4617,8 +4617,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 @@ -6031,8 +6031,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 --- 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 #include +#include "MoveOnly.h" #include "test_macros.h" #include "test_iterators.h" #include "types.h" @@ -47,6 +48,11 @@ }); } +struct MoveOnlyFunction : public MoveOnly { + template + constexpr T operator()(T x) const { return x + 42; } +}; + struct NonConstView : std::ranges::view_base { explicit NonConstView(int *b, int *e) : b_(b), e_(e) {} const int *begin() { return b_; } // deliberately non-const @@ -88,5 +94,14 @@ assert(std::equal(upp.begin(), upp.end(), check.begin(), check.end())); } + // [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[4] = {43, 44, 45, 46}; + assert(std::equal(transformed.begin(), transformed.end(), expected, expected + 4)); + } + 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 --- 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); -// Can't invoke with a move-only type. -static_assert(!std::is_invocable_v); - +#if _LIBCPP_STD_VER >= 23 +// Can invoke with a move-only type. +static_assert(std::is_invocable_v); +#endif constexpr bool test() { // Lvalue. { diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -716,7 +716,7 @@ }, { "name": "__cpp_lib_ranges", - "values": {"c++20": 202106}, + "values": {"c++20": 202207}, "headers": ["algorithm", "functional", "iterator", "memory", "ranges"], }, {