From 9f6439f1c5a3e98d03b7416449a256062e420003 Mon Sep 17 00:00:00 2001 From: yronglin Date: Sun, 25 Jun 2023 08:14:55 +0800 Subject: [PATCH] [libc++][ranges] Implement P2494R2 (Relaxing range adaptors to allow for move only types) Implement P2494R2 `Relaxing range adaptors to allow for move only types` https://open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2494r2.html#wording-ftm According to the words in P2494R2, I haven't add new test for `drop_while_view`, `take_while_view` and `filter_view`, because these views has the requirement that the predicate is an `indirect_unary_predicate`, which requires that the predicate is `copy_constructible`, so they still can't accept move only types as predicate. ``` [P2483R0] also suggests future work to relax the requirements on the predicate types stored by standard views. This paper does not perform this relaxation, as the copy constructibility requirement is enshrined in the indirect callable concepts ([indirectcallable.indirectinvocable]). Thus, while this paper modifies the views that currently use copyable-box for user provided predicates, it only does so to apply the rename of the exposition-only type to movable-box; it does not change any of the constraints on those views. It does, however, relax the requirements on invocables accepted by the transform family of views, because those are not constrained using the indirect callable concepts. ``` Reviewed By: #libc, var-const Differential Revision: https://reviews.llvm.org/D151629 --- libcxx/docs/FeatureTestMacroTable.rst | 2 +- libcxx/docs/Status/Cxx23Papers.csv | 2 +- libcxx/include/CMakeLists.txt | 2 +- libcxx/include/__ranges/copyable_box.h | 182 ------------------ libcxx/include/__ranges/drop_while_view.h | 4 +- libcxx/include/__ranges/filter_view.h | 4 +- libcxx/include/__ranges/iota_view.h | 2 +- libcxx/include/__ranges/movable_box.h | 206 +++++++++++++++++++++ libcxx/include/__ranges/single_view.h | 70 +++---- libcxx/include/__ranges/take_while_view.h | 4 +- libcxx/include/__ranges/transform_view.h | 29 ++- libcxx/include/module.modulemap.in | 2 +- libcxx/include/version | 4 +- .../range.copy.wrap/properties.compile.pass.cpp | 45 ----- .../arrow.pass.cpp | 10 +- .../assign.copy.pass.cpp | 16 +- .../assign.move.pass.cpp | 30 +-- .../ctor.default.pass.cpp | 8 +- .../ctor.in_place.pass.cpp | 16 +- .../deref.pass.cpp | 10 +- .../has_value.pass.cpp | 10 +- .../no_unique_address.pass.cpp | 12 +- .../range.move.wrap/properties.compile.pass.cpp | 61 ++++++ .../{range.copy.wrap => range.move.wrap}/types.h | 71 ++++--- .../algorithm.version.compile.pass.cpp | 14 +- .../functional.version.compile.pass.cpp | 14 +- .../iterator.version.compile.pass.cpp | 14 +- .../memory.version.compile.pass.cpp | 14 +- .../ranges.version.compile.pass.cpp | 14 +- .../version.version.compile.pass.cpp | 14 +- .../range.transform/general.pass.cpp | 20 ++ .../range.factories/range.single.view/cpo.pass.cpp | 7 +- libcxx/utils/data/ignore_format.txt | 1 - .../generate_feature_test_macro_components.py | 2 +- llvm/utils/gn/secondary/libcxx/include/BUILD.gn | 2 +- 35 files changed, 495 insertions(+), 423 deletions(-) delete mode 100644 libcxx/include/__ranges/copyable_box.h create mode 100644 libcxx/include/__ranges/movable_box.h delete mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/arrow.pass.cpp (82%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/assign.copy.pass.cpp (89%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/assign.move.pass.cpp (84%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/ctor.default.pass.cpp (89%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/ctor.in_place.pass.cpp (73%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/deref.pass.cpp (80%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/has_value.pass.cpp (79%) rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/no_unique_address.pass.cpp (86%) create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp rename libcxx/test/libcxx/ranges/range.adaptors/{range.copy.wrap => range.move.wrap}/types.h (79%) diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 9fa4495..deb3b3d 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -264,7 +264,7 @@ Status --------------------------------------------------- ----------------- ``__cpp_lib_polymorphic_allocator`` ``201902L`` --------------------------------------------------- ----------------- - ``__cpp_lib_ranges`` ``202106L`` + ``__cpp_lib_ranges`` ``202207L`` --------------------------------------------------- ----------------- ``__cpp_lib_remove_cvref`` ``201711L`` --------------------------------------------------- ----------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index c38327b..034ea99 100644 --- 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/CMakeLists.txt b/libcxx/include/CMakeLists.txt index a8e4011..7de36c0 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -609,7 +609,6 @@ set(files __ranges/common_view.h __ranges/concepts.h __ranges/container_compatible_range.h - __ranges/copyable_box.h __ranges/counted.h __ranges/dangling.h __ranges/data.h @@ -626,6 +625,7 @@ set(files __ranges/istream_view.h __ranges/join_view.h __ranges/lazy_split_view.h + __ranges/movable_box.h __ranges/non_propagating_cache.h __ranges/owning_view.h __ranges/range_adaptor.h diff --git a/libcxx/include/__ranges/copyable_box.h b/libcxx/include/__ranges/copyable_box.h deleted file mode 100644 index 9fbbb1e..0000000 --- a/libcxx/include/__ranges/copyable_box.h +++ /dev/null @@ -1,182 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP___RANGES_COPYABLE_BOX_H -#define _LIBCPP___RANGES_COPYABLE_BOX_H - -#include <__concepts/constructible.h> -#include <__concepts/copyable.h> -#include <__concepts/movable.h> -#include <__config> -#include <__memory/addressof.h> -#include <__memory/construct_at.h> -#include <__type_traits/is_nothrow_constructible.h> -#include <__type_traits/is_nothrow_copy_constructible.h> -#include <__type_traits/is_nothrow_default_constructible.h> -#include <__utility/move.h> -#include - -#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 -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 - 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 - 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> { - _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) - noexcept(is_nothrow_constructible_v<_Tp, _Args...>) - : __val_(std::forward<_Args>(__args)...) - { } - - _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) - requires default_initializable<_Tp> - : __val_() - { } - - _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box const&) = default; - _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box&&) = default; - - // Implementation of assignment operators in case we perform optimization (1) - _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box const&) requires copyable<_Tp> = default; - _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default; - - // Implementation of assignment operators in case we perform optimization (2) - _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box& operator=(__copyable_box const& __other) noexcept { - static_assert(is_nothrow_copy_constructible_v<_Tp>); - if (this != std::addressof(__other)) { - std::destroy_at(std::addressof(__val_)); - std::construct_at(std::addressof(__val_), __other.__val_); - } - return *this; - } - - _LIBCPP_HIDE_FROM_ABI - constexpr __copyable_box& operator=(__copyable_box&& __other) noexcept { - static_assert(is_nothrow_move_constructible_v<_Tp>); - if (this != std::addressof(__other)) { - std::destroy_at(std::addressof(__val_)); - std::construct_at(std::addressof(__val_), std::move(__other.__val_)); - } - return *this; - } - - _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; } - _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; } - - _LIBCPP_HIDE_FROM_ABI constexpr const _Tp *operator->() const noexcept { return std::addressof(__val_); } - _LIBCPP_HIDE_FROM_ABI constexpr _Tp *operator->() noexcept { return std::addressof(__val_); } - - _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; } - }; -} // namespace ranges - -#endif // _LIBCPP_STD_VER >= 20 - -_LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP___RANGES_COPYABLE_BOX_H diff --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h index 518feae..2f5fdb5 100644 --- a/libcxx/include/__ranges/drop_while_view.h +++ b/libcxx/include/__ranges/drop_while_view.h @@ -20,8 +20,8 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/concepts.h> -#include <__ranges/copyable_box.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/movable_box.h> #include <__ranges/non_propagating_cache.h> #include <__ranges/range_adaptor.h> #include <__ranges/view_interface.h> @@ -82,7 +82,7 @@ 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>, __empty_cache>; diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h index 4acbfad..77c3553 100644 --- a/libcxx/include/__ranges/filter_view.h +++ b/libcxx/include/__ranges/filter_view.h @@ -28,7 +28,7 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/concepts.h> -#include <__ranges/copyable_box.h> +#include <__ranges/movable_box.h> #include <__ranges/non_propagating_cache.h> #include <__ranges/range_adaptor.h> #include <__ranges/view_interface.h> @@ -53,7 +53,7 @@ namespace ranges { requires view<_View> && is_object_v<_Pred> class filter_view : public view_interface> { _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); - _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_; + _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_; // We cache the result of begin() to allow providing an amortized O(1) begin() whenever // the underlying range is at least a forward_range. diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h index ebfa022..a08fde2 100644 --- a/libcxx/include/__ranges/iota_view.h +++ b/libcxx/include/__ranges/iota_view.h @@ -27,8 +27,8 @@ #include <__iterator/incrementable_traits.h> #include <__iterator/iterator_traits.h> #include <__iterator/unreachable_sentinel.h> -#include <__ranges/copyable_box.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/movable_box.h> #include <__ranges/view_interface.h> #include <__type_traits/conditional.h> #include <__type_traits/is_nothrow_copy_constructible.h> diff --git a/libcxx/include/__ranges/movable_box.h b/libcxx/include/__ranges/movable_box.h new file mode 100644 index 0000000..8b3716a --- /dev/null +++ b/libcxx/include/__ranges/movable_box.h @@ -0,0 +1,206 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___RANGES_MOVABLE_BOX_H +#define _LIBCPP___RANGES_MOVABLE_BOX_H + +#include <__concepts/constructible.h> +#include <__concepts/copyable.h> +#include <__concepts/movable.h> +#include <__config> +#include <__memory/addressof.h> +#include <__memory/construct_at.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_nothrow_copy_constructible.h> +#include <__type_traits/is_nothrow_default_constructible.h> +#include <__utility/move.h> +#include + +#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 +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 + 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 +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 +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 __movable_box(in_place_t, _Args&&... __args) noexcept( + is_nothrow_constructible_v<_Tp, _Args...>) + : __val_(std::forward<_Args>(__args)...) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) + requires default_initializable<_Tp> + : __val_() {} + + _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default; + _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; + + // Implementation of assignment operators in case we perform optimization (1) + _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box const&) + requires copyable<_Tp> + = default; + _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&) + requires movable<_Tp> + = default; + + // Implementation of assignment operators in case we perform optimization (2) + _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box const& __other) noexcept { + static_assert(is_nothrow_copy_constructible_v<_Tp>); + if (this != std::addressof(__other)) { + std::destroy_at(std::addressof(__val_)); + std::construct_at(std::addressof(__val_), __other.__val_); + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box&& __other) noexcept { + static_assert(is_nothrow_move_constructible_v<_Tp>); + if (this != std::addressof(__other)) { + std::destroy_at(std::addressof(__val_)); + std::construct_at(std::addressof(__val_), std::move(__other.__val_)); + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; } + _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; } + + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return std::addressof(__val_); } + _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return std::addressof(__val_); } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; } +}; +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_MOVABLE_BOX_H diff --git a/libcxx/include/__ranges/single_view.h b/libcxx/include/__ranges/single_view.h index ccab2c1..5724e2d 100644 --- a/libcxx/include/__ranges/single_view.h +++ b/libcxx/include/__ranges/single_view.h @@ -12,7 +12,7 @@ #include <__concepts/constructible.h> #include <__config> -#include <__ranges/copyable_box.h> +#include <__ranges/movable_box.h> #include <__ranges/range_adaptor.h> #include <__ranges/view_interface.h> #include <__type_traits/decay.h> @@ -31,48 +31,48 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 namespace ranges { - template - requires is_object_v<_Tp> - class single_view : public view_interface> { - __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 - requires constructible_from<_Tp, _Args...> - _LIBCPP_HIDE_FROM_ABI - constexpr explicit single_view(in_place_t, _Args&&... __args) +# if _LIBCPP_STD_VER >= 23 +template +# else +template +# endif + requires is_object_v<_Tp> +class single_view : public view_interface> { + __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 + 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 single_view(_Tp) -> single_view<_Tp>; diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h index d1f1bfe..b4bdd18 100644 --- a/libcxx/include/__ranges/take_while_view.h +++ b/libcxx/include/__ranges/take_while_view.h @@ -20,7 +20,7 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/concepts.h> -#include <__ranges/copyable_box.h> +#include <__ranges/movable_box.h> #include <__ranges/range_adaptor.h> #include <__ranges/view_interface.h> #include <__type_traits/decay.h> @@ -60,7 +60,7 @@ class take_while_view : public view_interface> { class __sentinel; _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); - _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_; + _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_; public: _LIBCPP_HIDE_FROM_ABI take_while_view() diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h index 8bdfa97..dd346fb 100644 --- a/libcxx/include/__ranges/transform_view.h +++ b/libcxx/include/__ranges/transform_view.h @@ -26,8 +26,8 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/concepts.h> -#include <__ranges/copyable_box.h> #include <__ranges/empty.h> +#include <__ranges/movable_box.h> #include <__ranges/range_adaptor.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> @@ -62,13 +62,17 @@ concept __transform_view_constraints = regular_invocable<_Fn&, range_reference_t<_View>> && __can_reference>>; -template +# 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,11 +165,14 @@ struct __transform_view_iterator_category_base<_View, _Fn> { >; }; -template +# if _LIBCPP_STD_VER >= 23 +template +# else +template +# endif requires __transform_view_constraints<_View, _Fn> -template -class transform_view<_View, _Fn>::__iterator - : public __transform_view_iterator_category_base<_View, _Fn> { +template +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 +# if _LIBCPP_STD_VER >= 23 +template +# else +template +# endif requires __transform_view_constraints<_View, _Fn> -template +template class transform_view<_View, _Fn>::__sentinel { using _Parent = __maybe_const<_Const, transform_view>; using _Base = __maybe_const<_Const, _View>; diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index cdfb0e9..d49e339 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1359,7 +1359,6 @@ module std [system] { module common_view { private header "__ranges/common_view.h" } module concepts { private header "__ranges/concepts.h" } module container_compatible_range { private header "__ranges/container_compatible_range.h" } - module copyable_box { private header "__ranges/copyable_box.h" } module counted { private header "__ranges/counted.h" export span @@ -1382,6 +1381,7 @@ module std [system] { } module join_view { private header "__ranges/join_view.h" } module lazy_split_view { private header "__ranges/lazy_split_view.h" } + module movable_box { private header "__ranges/movable_box.h" } module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module owning_view { private header "__ranges/owning_view.h" } module range_adaptor { private header "__ranges/range_adaptor.h" } diff --git a/libcxx/include/version b/libcxx/include/version index ac93206..4091a49 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -150,7 +150,7 @@ __cpp_lib_out_ptr 202106L __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 @@ -381,7 +381,7 @@ __cpp_lib_within_lifetime 202306L # if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR) # define __cpp_lib_polymorphic_allocator 201902L # endif -# define __cpp_lib_ranges 202106L +# define __cpp_lib_ranges 202207L # define __cpp_lib_remove_cvref 201711L # if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC) # define __cpp_lib_semaphore 201907L diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp deleted file mode 100644 index 0b9a050..0000000 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -// Test various properties of - -#include - -#include - -#include "types.h" - -template -constexpr bool valid_copyable_box = requires { - typename std::ranges::__copyable_box; -}; - -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); // not an object type -static_assert(!valid_copyable_box); // not an object type -static_assert(!valid_copyable_box); - -// primary template -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(std::optional)); - -// optimization #1 -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(Copyable)); -static_assert(alignof(std::ranges::__copyable_box) == alignof(Copyable)); - -// optimization #2 -static_assert(sizeof(std::ranges::__copyable_box) == sizeof(NothrowCopyConstructible)); -static_assert(alignof(std::ranges::__copyable_box) == alignof(NothrowCopyConstructible)); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp similarity index 82% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp index e82840f..a676428 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/arrow.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/arrow.pass.cpp @@ -19,11 +19,11 @@ #include "types.h" -template +template 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 @@ constexpr void check() { // 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); @@ -41,8 +41,8 @@ constexpr void check() { } constexpr bool test() { - check(); // primary template - check(); // optimization #1 + check(); // primary template + check(); // optimization #1 check(); // optimization #2 return true; } diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp similarity index 89% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp index 0f6806b..94c4dec 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.copy.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.copy.pass.cpp @@ -24,8 +24,8 @@ constexpr bool test() { // Test the primary template { - using Box = std::ranges::__copyable_box; - static_assert( std::is_copy_assignable_v); + using Box = std::ranges::__movable_box; + static_assert(std::is_copy_assignable_v); static_assert(!std::is_nothrow_copy_assignable_v); { @@ -51,8 +51,8 @@ constexpr bool test() { // Test optimization #1 for copy-assignment { - using Box = std::ranges::__copyable_box; - static_assert( std::is_copy_assignable_v); + 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 @@ constexpr bool test() { // 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 @@ constexpr bool test() { // 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 { @@ -137,7 +137,7 @@ void test_empty_state() { } // assign empty to empty { - Box x = create_empty_box(); + Box x = create_empty_box(); Box const y = create_empty_box(); Box& result = (x = y); @@ -147,7 +147,7 @@ void test_empty_state() { } // check self-assignment in empty case { - Box x = create_empty_box(); + Box x = create_empty_box(); Box& result = (x = x); assert(&result == &x); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp similarity index 84% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp index bb46401..d4dc612 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/assign.move.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/assign.move.pass.cpp @@ -24,8 +24,8 @@ constexpr bool test() { // Test the primary template { - using Box = std::ranges::__copyable_box; - static_assert( std::is_move_assignable_v); + using Box = std::ranges::__movable_box; + static_assert(std::is_move_assignable_v); static_assert(!std::is_nothrow_move_assignable_v); { @@ -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; + 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); + static_assert( + std::is_nothrow_move_assignable_v == std::is_nothrow_move_assignable_v); { 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; - static_assert( std::is_move_assignable_v); + using Box = std::ranges::__movable_box; + static_assert(std::is_move_assignable_v); static_assert(!std::is_nothrow_move_assignable_v); { @@ -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; + 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); + static_assert( + std::is_nothrow_move_assignable_v == std::is_nothrow_move_assignable_v); { 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; + using Box = std::ranges::__movable_box; static_assert(std::is_move_assignable_v); static_assert(std::is_nothrow_move_assignable_v); @@ -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; + using Box = std::ranges::__movable_box; // assign non-empty to empty { @@ -186,7 +188,7 @@ void test_empty_state() { // assign empty to non-empty { Box x(std::in_place, 5); - Box y = create_empty_box(); + Box y = create_empty_box(); Box& result = (x = std::move(y)); assert(&result == &x); @@ -195,8 +197,8 @@ void test_empty_state() { } // assign empty to empty { - Box x = create_empty_box(); - Box y = create_empty_box(); + Box x = create_empty_box(); + Box y = create_empty_box(); Box& result = (x = std::move(y)); assert(&result == &x); @@ -205,7 +207,7 @@ void test_empty_state() { } // check self-assignment in empty case { - Box x = create_empty_box(); + Box x = create_empty_box(); Box& result = (x = std::move(x)); assert(&result == &x); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp similarity index 89% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp index c4d0957..3014dc0 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.default.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.default.pass.cpp @@ -18,19 +18,19 @@ #include "types.h" -template -using Box = std::ranges::__copyable_box; +template +using Box = std::ranges::__movable_box; struct NoDefault { NoDefault() = delete; }; static_assert(!std::is_default_constructible_v>); -template +template struct DefaultNoexcept { DefaultNoexcept() noexcept(Noexcept); }; -static_assert( std::is_nothrow_default_constructible_v>>); +static_assert(std::is_nothrow_default_constructible_v>>); static_assert(!std::is_nothrow_default_constructible_v>>); constexpr bool test() { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp similarity index 73% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp index 767d7cc..6f97b7e 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/ctor.in_place.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/ctor.in_place.pass.cpp @@ -19,9 +19,9 @@ #include "types.h" -struct UnknownType { }; +struct UnknownType {}; -template +template 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; + using Box = std::ranges::__movable_box; 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; + using Box = std::ranges::__movable_box; 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; + 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.move.wrap/deref.pass.cpp similarity index 80% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp index 075d578..37734f0 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/deref.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/deref.pass.cpp @@ -19,11 +19,11 @@ #include "types.h" -template +template 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 @@ constexpr void check() { // 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); @@ -39,8 +39,8 @@ constexpr void check() { } constexpr bool test() { - check(); // primary template - check(); // optimization #1 + check(); // primary template + check(); // optimization #1 check(); // optimization #2 return true; } diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp similarity index 79% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp index aa68f23..397e638 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/has_value.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/has_value.pass.cpp @@ -18,15 +18,15 @@ #include "types.h" -template +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()); } constexpr bool test() { - check(); // primary template - check(); // optimization #1 + check(); // primary template + check(); // optimization #1 check(); // 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 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.move.wrap/no_unique_address.pass.cpp similarity index 86% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp index 072527b..e21191f9 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/no_unique_address.pass.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/no_unique_address.pass.cpp @@ -16,19 +16,19 @@ #include 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; +using Box = std::ranges::__movable_box; -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& base = static_cast(box); Box& member = static_cast(box.member); // Despite [[no_unique_address]], the two objects have the same type so they diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp new file mode 100644 index 0000000..6596c70 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/properties.compile.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// Test various properties of + +#include + +#include + +#include "MoveOnly.h" + +#include "types.h" + +template +constexpr bool valid_movable_box = requires { typename std::ranges::__movable_box; }; + +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); // 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::__movable_box) == sizeof(std::optional)); + +// optimization #1 +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::__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.move.wrap/types.h similarity index 79% rename from libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h rename to libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h index bcc3493..9e6bf5d 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/types.h +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h @@ -16,15 +16,15 @@ #include "test_macros.h" -// NOTE: These types are strongly tied to the implementation of __copyable_box. See the documentation -// in __copyable_box for the meaning of optimizations #1 and #2. +// NOTE: These types are strongly tied to the implementation of __movable_box. See the documentation +// in __movable_box for the meaning of optimizations #1 and #2. // Copy constructible, but neither copyable nor nothrow_copy/move_constructible. This uses the primary template. struct CopyConstructible { constexpr CopyConstructible() = default; - constexpr explicit CopyConstructible(int x) : value(x) { } + constexpr explicit CopyConstructible(int x) : value(x) {} CopyConstructible(CopyConstructible const&) noexcept(false) = default; - CopyConstructible& operator=(CopyConstructible const&) = delete; + CopyConstructible& operator=(CopyConstructible const&) = delete; int value = -1; }; @@ -33,105 +33,102 @@ static_assert(!std::is_nothrow_copy_constructible_v); static_assert(!std::movable); static_assert(!std::is_nothrow_move_constructible_v); - // 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); +static_assert(std::copyable); static_assert(!std::is_nothrow_copy_constructible_v); -static_assert( std::movable); +static_assert(std::movable); static_assert(!std::is_nothrow_move_constructible_v); - // 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); -static_assert( std::is_nothrow_copy_constructible_v); +static_assert(std::is_nothrow_copy_constructible_v); static_assert(!std::movable); -static_assert( std::is_nothrow_move_constructible_v); - +static_assert(std::is_nothrow_move_constructible_v); // 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); -static_assert( std::is_nothrow_copy_constructible_v); -static_assert( std::movable); +static_assert(std::is_nothrow_copy_constructible_v); +static_assert(std::movable); static_assert(!std::is_nothrow_move_constructible_v); - #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 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 index d93c636..1cd58f0 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp @@ -19,7 +19,7 @@ __cpp_lib_clamp 201603L [C++17] __cpp_lib_constexpr_algorithms 201806L [C++20] __cpp_lib_parallel_algorithm 201603L [C++17] - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_ranges_starts_ends_with 202106L [C++23] __cpp_lib_robust_nonmodifying_seq_ops 201304L [C++14] __cpp_lib_sample 201603L [C++17] @@ -184,8 +184,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifdef __cpp_lib_ranges_starts_ends_with @@ -245,8 +245,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -315,8 +315,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp index f14b127..72c96c6 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp @@ -28,7 +28,7 @@ __cpp_lib_invoke_r 202106L [C++23] __cpp_lib_move_only_function 202110L [C++23] __cpp_lib_not_fn 201603L [C++17] - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_result_of_sfinae 201210L [C++14] __cpp_lib_transparent_operators 201210L [C++14] 201510L [C++17] @@ -293,8 +293,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifndef __cpp_lib_result_of_sfinae @@ -399,8 +399,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # ifndef __cpp_lib_result_of_sfinae @@ -523,8 +523,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # ifndef __cpp_lib_result_of_sfinae diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp index 2a292dc..700907c 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp @@ -23,7 +23,7 @@ __cpp_lib_move_iterator_concept 202207L [C++20] __cpp_lib_nonmember_container_access 201411L [C++17] __cpp_lib_null_iterators 201304L [C++14] - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_ssize 201902L [C++20] */ @@ -197,8 +197,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifndef __cpp_lib_ssize @@ -255,8 +255,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # ifndef __cpp_lib_ssize @@ -313,8 +313,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # ifndef __cpp_lib_ssize diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp index 02559d1..94cb497 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp @@ -27,7 +27,7 @@ __cpp_lib_enable_shared_from_this 201603L [C++17] __cpp_lib_make_unique 201304L [C++14] __cpp_lib_out_ptr 202106L [C++23] - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_raw_memory_algorithms 201606L [C++17] __cpp_lib_shared_ptr_arrays 201611L [C++17] 201707L [C++20] @@ -363,8 +363,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifndef __cpp_lib_raw_memory_algorithms @@ -500,8 +500,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # ifndef __cpp_lib_raw_memory_algorithms @@ -637,8 +637,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # ifndef __cpp_lib_raw_memory_algorithms diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index 88e059d..7a1edd1 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -16,7 +16,7 @@ // Test the feature test macros defined by /* Constant Value - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] @@ -123,8 +123,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifdef __cpp_lib_ranges_as_rvalue @@ -156,8 +156,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # ifndef __cpp_lib_ranges_as_rvalue @@ -237,8 +237,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # ifndef __cpp_lib_ranges_as_rvalue diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 0630162..e969d22 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -139,7 +139,7 @@ __cpp_lib_parallel_algorithm 201603L [C++17] __cpp_lib_polymorphic_allocator 201902L [C++20] __cpp_lib_quoted_string_io 201304L [C++14] - __cpp_lib_ranges 202106L [C++20] + __cpp_lib_ranges 202207L [C++20] __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] @@ -3540,8 +3540,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++20" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++20" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++20" # endif # ifdef __cpp_lib_ranges_as_rvalue @@ -4899,8 +4899,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++23" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++23" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++23" # endif # ifndef __cpp_lib_ranges_as_rvalue @@ -6438,8 +6438,8 @@ # ifndef __cpp_lib_ranges # error "__cpp_lib_ranges should be defined in c++26" # endif -# if __cpp_lib_ranges != 202106L -# error "__cpp_lib_ranges should have the value 202106L in c++26" +# if __cpp_lib_ranges != 202207L +# error "__cpp_lib_ranges should have the value 202207L in c++26" # endif # ifndef __cpp_lib_ranges_as_rvalue diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp index f48aae6..32fed6f 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp @@ -22,6 +22,7 @@ #include #include +#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 + constexpr T operator()(T x) const { + return x + 42; + } +}; +#endif + struct NonConstView : std::ranges::view_base { explicit NonConstView(int *b, int *e) : b_(b), e_(e) {} const int *begin() { return b_; } // deliberately non-const @@ -87,6 +97,16 @@ int main(int, char**) { std::string_view check = "HELLO, WORLD."; assert(std::equal(upp.begin(), upp.end(), check.begin(), check.end())); } +#if _LIBCPP_STD_VER >= 23 + // [P2494R2] Relaxing range adaptors to allow for move only types. + // Test transform_view is valid when the function object is a move only type. + { + int a[] = {1, 2, 3, 4}; + auto transformed = NonConstView(a, a + 4) | std::views::transform(MoveOnlyFunction()); + int expected[] = {43, 44, 45, 46}; + assert(std::equal(transformed.begin(), transformed.end(), expected, expected + 4)); + } +#endif return 0; } diff --git a/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp b/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp index d0cd239..d818a74 100644 --- a/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp +++ b/libcxx/test/std/ranges/range.factories/range.single.view/cpo.pass.cpp @@ -19,9 +19,10 @@ // Can't invoke without arguments. static_assert(!std::is_invocable_v); -// 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/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt index f43f1cc..c995119 100644 --- a/libcxx/utils/data/ignore_format.txt +++ b/libcxx/utils/data/ignore_format.txt @@ -413,7 +413,6 @@ libcxx/include/__ranges/access.h libcxx/include/__ranges/all.h libcxx/include/__ranges/common_view.h libcxx/include/__ranges/concepts.h -libcxx/include/__ranges/copyable_box.h libcxx/include/__ranges/counted.h libcxx/include/__ranges/data.h libcxx/include/__ranges/drop_view.h diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index c30fb33..3f4ba0e 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -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"], }, { diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn index 068f87d..3b4bd9e 100644 --- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn +++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn @@ -680,7 +680,7 @@ if (current_toolchain == default_toolchain) { "__ranges/common_view.h", "__ranges/concepts.h", "__ranges/container_compatible_range.h", - "__ranges/copyable_box.h", + "__ranges/movable_box.h", "__ranges/counted.h", "__ranges/dangling.h", "__ranges/data.h", -- 2.7.4