1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10
11 // <copyable-box>& operator=(<copyable-box>&&)
12
13 // ADDITIONAL_COMPILE_FLAGS: -Wno-self-move
14
15 #include <ranges>
16
17 #include <cassert>
18 #include <type_traits>
19 #include <utility> // in_place_t
20
21 #include "test_macros.h"
22 #include "types.h"
23
test()24 constexpr bool test() {
25 // Test the primary template
26 {
27 using Box = std::ranges::__copyable_box<CopyConstructible>;
28 static_assert( std::is_move_assignable_v<Box>);
29 static_assert(!std::is_nothrow_move_assignable_v<Box>);
30
31 {
32 Box x(std::in_place, 5);
33 Box y(std::in_place, 10);
34 Box& result = (x = std::move(y));
35
36 assert(&result == &x);
37 assert(x.__has_value());
38 assert(y.__has_value());
39 assert((*x).value == 10);
40 }
41 // check self-assignment
42 {
43 Box x(std::in_place, 5);
44 Box& result = (x = std::move(x));
45
46 assert(&result == &x);
47 assert(x.__has_value());
48 assert((*x).value == 5);
49 }
50 }
51
52 // Make sure that we use the native move assignment in the primary template if we can.
53 {
54 using Box = std::ranges::__copyable_box<CopyConstructibleMovable>;
55 static_assert(std::is_move_assignable_v<Box>);
56 static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<CopyConstructibleMovable>);
57
58 {
59 Box x(std::in_place, 5);
60 Box y(std::in_place, 10);
61 Box& result = (x = std::move(y));
62
63 assert(&result == &x);
64 assert(x.__has_value());
65 assert(y.__has_value());
66 assert((*x).value == 10);
67 assert((*x).did_move_assign);
68 }
69 // check self-assignment
70 {
71 Box x(std::in_place, 5);
72 Box& result = (x = std::move(x));
73
74 assert(&result == &x);
75 assert(x.__has_value());
76 assert((*x).value == 5);
77 assert((*x).did_move_assign);
78 }
79 }
80
81 // Test optimization #1 for move assignment
82 {
83 using Box = std::ranges::__copyable_box<Copyable>;
84 static_assert( std::is_move_assignable_v<Box>);
85 static_assert(!std::is_nothrow_move_assignable_v<Box>);
86
87 {
88 Box x(std::in_place, 5);
89 Box y(std::in_place, 10);
90 Box& result = (x = std::move(y));
91
92 assert(&result == &x);
93 assert(x.__has_value());
94 assert(y.__has_value());
95 assert((*x).value == 10);
96 assert((*x).did_move_assign);
97 }
98 // check self-assignment (should use the underlying type's assignment too)
99 {
100 Box x(std::in_place, 5);
101 Box& result = (x = std::move(x));
102
103 assert(&result == &x);
104 assert(x.__has_value());
105 assert((*x).value == 5);
106 assert((*x).did_move_assign);
107 }
108 }
109
110 // Test optimization #1 for move assignment with a type that uses optimization #2 for copy assignment
111 {
112 using Box = std::ranges::__copyable_box<MovableNothrowCopyConstructible>;
113 static_assert(std::is_move_assignable_v<Box>);
114 static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<MovableNothrowCopyConstructible>);
115
116 {
117 Box x(std::in_place, 5);
118 Box y(std::in_place, 10);
119 Box& result = (x = std::move(y));
120
121 assert(&result == &x);
122 assert(x.__has_value());
123 assert(y.__has_value());
124 assert((*x).value == 10);
125 assert((*x).did_move_assign);
126 }
127 // check self-assignment (should use the underlying type's assignment too)
128 {
129 Box x(std::in_place, 5);
130 Box& result = (x = std::move(x));
131
132 assert(&result == &x);
133 assert(x.__has_value());
134 assert((*x).value == 5);
135 assert((*x).did_move_assign);
136 }
137 }
138
139 // Test optimization #2 for move assignment
140 {
141 using Box = std::ranges::__copyable_box<NothrowCopyConstructible>;
142 static_assert(std::is_move_assignable_v<Box>);
143 static_assert(std::is_nothrow_move_assignable_v<Box>);
144
145 {
146 Box x(std::in_place, 5);
147 Box y(std::in_place, 10);
148 Box& result = (x = std::move(y));
149
150 assert(&result == &x);
151 assert(x.__has_value());
152 assert(y.__has_value());
153 assert((*x).value == 10);
154 }
155 // check self-assignment
156 {
157 Box x(std::in_place, 5);
158 Box& result = (x = std::move(x));
159
160 assert(&result == &x);
161 assert(x.__has_value());
162 assert((*x).value == 5);
163 }
164 }
165
166 return true;
167 }
168
169 // Tests for the empty state. Those can't be constexpr, since they are only reached
170 // through throwing an exception.
171 #if !defined(TEST_HAS_NO_EXCEPTIONS)
test_empty_state()172 void test_empty_state() {
173 using Box = std::ranges::__copyable_box<ThrowsOnCopy>;
174
175 // assign non-empty to empty
176 {
177 Box x = create_empty_box();
178 Box y(std::in_place, 10);
179 Box& result = (x = std::move(y));
180
181 assert(&result == &x);
182 assert(x.__has_value());
183 assert(y.__has_value());
184 assert((*x).value == 10);
185 }
186 // assign empty to non-empty
187 {
188 Box x(std::in_place, 5);
189 Box y = create_empty_box();
190 Box& result = (x = std::move(y));
191
192 assert(&result == &x);
193 assert(!x.__has_value());
194 assert(!y.__has_value());
195 }
196 // assign empty to empty
197 {
198 Box x = create_empty_box();
199 Box y = create_empty_box();
200 Box& result = (x = std::move(y));
201
202 assert(&result == &x);
203 assert(!x.__has_value());
204 assert(!y.__has_value());
205 }
206 // check self-assignment in empty case
207 {
208 Box x = create_empty_box();
209 Box& result = (x = std::move(x));
210
211 assert(&result == &x);
212 assert(!x.__has_value());
213 }
214 }
215 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
216
main(int,char **)217 int main(int, char**) {
218 assert(test());
219 static_assert(test());
220
221 #if !defined(TEST_HAS_NO_EXCEPTIONS)
222 test_empty_state();
223 #endif
224
225 return 0;
226 }
227