| 1 | // -*- C++ -*- |
| 2 | //===----------------------------------------------------------------------===// |
| 3 | // |
| 4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 5 | // See https://llvm.org/LICENSE.txt for license information. |
| 6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #ifndef _LIBCPP___RANGES_MOVABLE_BOX_H |
| 11 | #define _LIBCPP___RANGES_MOVABLE_BOX_H |
| 12 | |
| 13 | #include <__concepts/constructible.h> |
| 14 | #include <__concepts/copyable.h> |
| 15 | #include <__concepts/movable.h> |
| 16 | #include <__config> |
| 17 | #include <__memory/addressof.h> |
| 18 | #include <__memory/construct_at.h> |
| 19 | #include <__type_traits/is_nothrow_constructible.h> |
| 20 | #include <__utility/move.h> |
| 21 | #include <optional> |
| 22 | |
| 23 | #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
| 24 | # pragma GCC system_header |
| 25 | #endif |
| 26 | |
| 27 | _LIBCPP_PUSH_MACROS |
| 28 | #include <__undef_macros> |
| 29 | |
| 30 | _LIBCPP_BEGIN_NAMESPACE_STD |
| 31 | |
| 32 | #if _LIBCPP_STD_VER >= 20 |
| 33 | |
| 34 | // __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into |
| 35 | // a type that is both move-constructible and move-assignable. It does that by introducing an empty state |
| 36 | // and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary |
| 37 | // to handle the case where the copy construction fails after destroying the object. |
| 38 | // |
| 39 | // In some cases, we can completely avoid the use of an empty state; we provide a specialization of |
| 40 | // __movable_box that does this, see below for the details. |
| 41 | |
| 42 | // until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not |
| 43 | // just move-constructible; we preserve the old behavior in pre-C++23 modes. |
| 44 | template <class _Tp> |
| 45 | concept __movable_box_object = |
| 46 | # if _LIBCPP_STD_VER >= 23 |
| 47 | move_constructible<_Tp> |
| 48 | # else |
| 49 | copy_constructible<_Tp> |
| 50 | # endif |
| 51 | && is_object_v<_Tp>; |
| 52 | |
| 53 | namespace ranges { |
| 54 | // Primary template - uses std::optional and introduces an empty state in case assignment fails. |
| 55 | template <__movable_box_object _Tp> |
| 56 | class __movable_box { |
| 57 | _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_; |
| 58 | |
| 59 | public: |
| 60 | template <class... _Args> |
| 61 | requires is_constructible_v<_Tp, _Args...> |
| 62 | _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept( |
| 63 | is_nothrow_constructible_v<_Tp, _Args...>) |
| 64 | : __val_(in_place, std::forward<_Args>(__args)...) {} |
| 65 | |
| 66 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) |
| 67 | requires default_initializable<_Tp> |
| 68 | : __val_(in_place) {} |
| 69 | |
| 70 | _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default; |
| 71 | _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; |
| 72 | |
| 73 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& |
| 74 | operator=(__movable_box const& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>) |
| 75 | # if _LIBCPP_STD_VER >= 23 |
| 76 | requires copy_constructible<_Tp> |
| 77 | # endif |
| 78 | { |
| 79 | if (this != std::addressof(__other)) { |
| 80 | if (__other.__has_value()) |
| 81 | __val_.emplace(*__other); |
| 82 | else |
| 83 | __val_.reset(); |
| 84 | } |
| 85 | return *this; |
| 86 | } |
| 87 | |
| 88 | _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&) |
| 89 | requires movable<_Tp> |
| 90 | = default; |
| 91 | |
| 92 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& |
| 93 | operator=(__movable_box&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) { |
| 94 | if (this != std::addressof(__other)) { |
| 95 | if (__other.__has_value()) |
| 96 | __val_.emplace(std::move(*__other)); |
| 97 | else |
| 98 | __val_.reset(); |
| 99 | } |
| 100 | return *this; |
| 101 | } |
| 102 | |
| 103 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; } |
| 104 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; } |
| 105 | |
| 106 | _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return __val_.operator->(); } |
| 107 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return __val_.operator->(); } |
| 108 | |
| 109 | _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); } |
| 110 | }; |
| 111 | |
| 112 | // This partial specialization implements an optimization for when we know we don't need to store |
| 113 | // an empty state to represent failure to perform an assignment. For copy-assignment, this happens: |
| 114 | // |
| 115 | // 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator |
| 116 | // directly and avoid using std::optional. |
| 117 | // 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as |
| 118 | // destroy-and-then-construct and we know it will never fail, so we don't need an empty state. |
| 119 | // |
| 120 | // The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and |
| 121 | // nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled |
| 122 | // whenever we can apply any of these optimizations for both the copy assignment and the move assignment |
| 123 | // operator. |
| 124 | |
| 125 | # if _LIBCPP_STD_VER >= 23 |
| 126 | template <class _Tp> |
| 127 | concept __doesnt_need_empty_state = |
| 128 | (copy_constructible<_Tp> |
| 129 | // 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models |
| 130 | // copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true. |
| 131 | ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>) |
| 132 | // 2. Otherwise, movable-box<T> should store only a T if either T models movable or |
| 133 | // is_nothrow_move_constructible_v<T> is true. |
| 134 | : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>); |
| 135 | |
| 136 | // When _Tp doesn't have an assignment operator, we must implement __movable_box's assignment operator |
| 137 | // by doing destroy_at followed by construct_at. However, that implementation strategy leads to UB if the nested |
| 138 | // _Tp is potentially overlapping, as it is doing a non-transparent replacement of the sub-object, which means that |
| 139 | // we're not considered "nested" inside the movable-box anymore, and since we're not nested within it, [basic.life]/1.5 |
| 140 | // says that we essentially just reused the storage of the movable-box for a completely unrelated object and ended the |
| 141 | // movable-box's lifetime. |
| 142 | // https://github.com/llvm/llvm-project/issues/70494#issuecomment-1845646490 |
| 143 | // |
| 144 | // Hence, when the _Tp doesn't have an assignment operator, we can't risk making it a potentially-overlapping |
| 145 | // subobject because of the above, and we don't use [[no_unique_address]] in that case. |
| 146 | template <class _Tp> |
| 147 | concept __can_use_no_unique_address = (copy_constructible<_Tp> ? copyable<_Tp> : movable<_Tp>); |
| 148 | |
| 149 | # else |
| 150 | |
| 151 | template <class _Tp> |
| 152 | concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>; |
| 153 | |
| 154 | template <class _Tp> |
| 155 | concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>; |
| 156 | |
| 157 | template <class _Tp> |
| 158 | concept __doesnt_need_empty_state = __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>; |
| 159 | |
| 160 | template <class _Tp> |
| 161 | concept __can_use_no_unique_address = copyable<_Tp>; |
| 162 | # endif |
| 163 | |
| 164 | template <class _Tp> |
| 165 | struct __movable_box_holder { |
| 166 | _Tp __val_; |
| 167 | |
| 168 | template <class... _Args> |
| 169 | _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box_holder(in_place_t, _Args&&... __args) |
| 170 | : __val_(std::forward<_Args>(__args)...) {} |
| 171 | }; |
| 172 | |
| 173 | template <class _Tp> |
| 174 | requires __can_use_no_unique_address<_Tp> |
| 175 | struct __movable_box_holder<_Tp> { |
| 176 | _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_; |
| 177 | |
| 178 | template <class... _Args> |
| 179 | _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box_holder(in_place_t, _Args&&... __args) |
| 180 | : __val_(std::forward<_Args>(__args)...) {} |
| 181 | }; |
| 182 | |
| 183 | template <__movable_box_object _Tp> |
| 184 | requires __doesnt_need_empty_state<_Tp> |
| 185 | class __movable_box<_Tp> { |
| 186 | _LIBCPP_NO_UNIQUE_ADDRESS __movable_box_holder<_Tp> __holder_; |
| 187 | |
| 188 | public: |
| 189 | template <class... _Args> |
| 190 | requires is_constructible_v<_Tp, _Args...> |
| 191 | _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t __inplace, _Args&&... __args) noexcept( |
| 192 | is_nothrow_constructible_v<_Tp, _Args...>) |
| 193 | : __holder_(__inplace, std::forward<_Args>(__args)...) {} |
| 194 | |
| 195 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) |
| 196 | requires default_initializable<_Tp> |
| 197 | : __holder_(in_place_t{}) {} |
| 198 | |
| 199 | _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default; |
| 200 | _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; |
| 201 | |
| 202 | // Implementation of assignment operators in case we perform optimization (1) |
| 203 | _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box const&) |
| 204 | requires copyable<_Tp> |
| 205 | = default; |
| 206 | _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&) |
| 207 | requires movable<_Tp> |
| 208 | = default; |
| 209 | |
| 210 | // Implementation of assignment operators in case we perform optimization (2) |
| 211 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box const& __other) noexcept { |
| 212 | static_assert(is_nothrow_copy_constructible_v<_Tp>); |
| 213 | static_assert(!__can_use_no_unique_address<_Tp>); |
| 214 | if (this != std::addressof(__other)) { |
| 215 | std::destroy_at(std::addressof(__holder_.__val_)); |
| 216 | std::construct_at(std::addressof(__holder_.__val_), __other.__holder_.__val_); |
| 217 | } |
| 218 | return *this; |
| 219 | } |
| 220 | |
| 221 | _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box&& __other) noexcept { |
| 222 | static_assert(is_nothrow_move_constructible_v<_Tp>); |
| 223 | static_assert(!__can_use_no_unique_address<_Tp>); |
| 224 | if (this != std::addressof(__other)) { |
| 225 | std::destroy_at(std::addressof(__holder_.__val_)); |
| 226 | std::construct_at(std::addressof(__holder_.__val_), std::move(__other.__holder_.__val_)); |
| 227 | } |
| 228 | return *this; |
| 229 | } |
| 230 | |
| 231 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __holder_.__val_; } |
| 232 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __holder_.__val_; } |
| 233 | |
| 234 | _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return std::addressof(__holder_.__val_); } |
| 235 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return std::addressof(__holder_.__val_); } |
| 236 | |
| 237 | _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; } |
| 238 | }; |
| 239 | } // namespace ranges |
| 240 | |
| 241 | #endif // _LIBCPP_STD_VER >= 20 |
| 242 | |
| 243 | _LIBCPP_END_NAMESPACE_STD |
| 244 | |
| 245 | _LIBCPP_POP_MACROS |
| 246 | |
| 247 | #endif // _LIBCPP___RANGES_MOVABLE_BOX_H |
| 248 | |