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
10
11 // Throwing bad_variant_access is supported starting in macosx10.13
12 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions
13
14 // <variant>
15
16 // template <class ...Types> class variant;
17
18 // variant& operator=(variant&&) noexcept(see below); // constexpr in C++20
19
20 #include <cassert>
21 #include <string>
22 #include <type_traits>
23 #include <utility>
24 #include <variant>
25
26 #include "test_macros.h"
27 #include "variant_test_helpers.h"
28
29 struct NoCopy {
30 NoCopy(const NoCopy &) = delete;
31 NoCopy &operator=(const NoCopy &) = default;
32 };
33
34 struct CopyOnly {
35 CopyOnly(const CopyOnly &) = default;
36 CopyOnly(CopyOnly &&) = delete;
37 CopyOnly &operator=(const CopyOnly &) = default;
38 CopyOnly &operator=(CopyOnly &&) = delete;
39 };
40
41 struct MoveOnly {
42 MoveOnly(const MoveOnly &) = delete;
43 MoveOnly(MoveOnly &&) = default;
44 MoveOnly &operator=(const MoveOnly &) = delete;
45 MoveOnly &operator=(MoveOnly &&) = default;
46 };
47
48 struct MoveOnlyNT {
49 MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT50 MoveOnlyNT(MoveOnlyNT &&) {}
51 MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
52 MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
53 };
54
55 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow56 MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
57 MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
58 MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
59 MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
60 };
61
62 struct MoveAssignOnly {
63 MoveAssignOnly(MoveAssignOnly &&) = delete;
64 MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
65 };
66
67 struct MoveAssign {
68 static int move_construct;
69 static int move_assign;
resetMoveAssign70 static void reset() { move_construct = move_assign = 0; }
MoveAssignMoveAssign71 MoveAssign(int v) : value(v) {}
MoveAssignMoveAssign72 MoveAssign(MoveAssign &&o) : value(o.value) {
73 ++move_construct;
74 o.value = -1;
75 }
operator =MoveAssign76 MoveAssign &operator=(MoveAssign &&o) {
77 value = o.value;
78 ++move_assign;
79 o.value = -1;
80 return *this;
81 }
82 int value;
83 };
84
85 int MoveAssign::move_construct = 0;
86 int MoveAssign::move_assign = 0;
87
88 struct NTMoveAssign {
NTMoveAssignNTMoveAssign89 constexpr NTMoveAssign(int v) : value(v) {}
90 NTMoveAssign(const NTMoveAssign &) = default;
91 NTMoveAssign(NTMoveAssign &&) = default;
92 NTMoveAssign &operator=(const NTMoveAssign &that) = default;
operator =NTMoveAssign93 NTMoveAssign &operator=(NTMoveAssign &&that) {
94 value = that.value;
95 that.value = -1;
96 return *this;
97 };
98 int value;
99 };
100
101 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
102 static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
103
104 struct TMoveAssign {
TMoveAssignTMoveAssign105 constexpr TMoveAssign(int v) : value(v) {}
106 TMoveAssign(const TMoveAssign &) = delete;
107 TMoveAssign(TMoveAssign &&) = default;
108 TMoveAssign &operator=(const TMoveAssign &) = delete;
109 TMoveAssign &operator=(TMoveAssign &&) = default;
110 int value;
111 };
112
113 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
114
115 struct TMoveAssignNTCopyAssign {
TMoveAssignNTCopyAssignTMoveAssignNTCopyAssign116 constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
117 TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default;
118 TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default;
operator =TMoveAssignNTCopyAssign119 TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) {
120 value = that.value;
121 return *this;
122 }
123 TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default;
124 int value;
125 };
126
127 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
128
129 struct TrivialCopyNontrivialMove {
130 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
TrivialCopyNontrivialMoveTrivialCopyNontrivialMove131 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
132 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
operator =TrivialCopyNontrivialMove133 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept {
134 return *this;
135 }
136 };
137
138 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
139 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
140
141
test_move_assignment_noexcept()142 void test_move_assignment_noexcept() {
143 {
144 using V = std::variant<int>;
145 static_assert(std::is_nothrow_move_assignable<V>::value, "");
146 }
147 {
148 using V = std::variant<MoveOnly>;
149 static_assert(std::is_nothrow_move_assignable<V>::value, "");
150 }
151 {
152 using V = std::variant<int, long>;
153 static_assert(std::is_nothrow_move_assignable<V>::value, "");
154 }
155 {
156 using V = std::variant<int, MoveOnly>;
157 static_assert(std::is_nothrow_move_assignable<V>::value, "");
158 }
159 {
160 using V = std::variant<MoveOnlyNT>;
161 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
162 }
163 {
164 using V = std::variant<MoveOnlyOddNothrow>;
165 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
166 }
167 }
168
test_move_assignment_sfinae()169 void test_move_assignment_sfinae() {
170 {
171 using V = std::variant<int, long>;
172 static_assert(std::is_move_assignable<V>::value, "");
173 }
174 {
175 using V = std::variant<int, CopyOnly>;
176 static_assert(std::is_move_assignable<V>::value, "");
177 }
178 {
179 using V = std::variant<int, NoCopy>;
180 static_assert(!std::is_move_assignable<V>::value, "");
181 }
182 {
183 using V = std::variant<int, MoveOnly>;
184 static_assert(std::is_move_assignable<V>::value, "");
185 }
186 {
187 using V = std::variant<int, MoveOnlyNT>;
188 static_assert(std::is_move_assignable<V>::value, "");
189 }
190 {
191 // variant only provides move assignment when the types also provide
192 // a move constructor.
193 using V = std::variant<int, MoveAssignOnly>;
194 static_assert(!std::is_move_assignable<V>::value, "");
195 }
196
197 // Make sure we properly propagate triviality (see P0602R4).
198 #if TEST_STD_VER > 17
199 {
200 using V = std::variant<int, long>;
201 static_assert(std::is_trivially_move_assignable<V>::value, "");
202 }
203 {
204 using V = std::variant<int, NTMoveAssign>;
205 static_assert(!std::is_trivially_move_assignable<V>::value, "");
206 static_assert(std::is_move_assignable<V>::value, "");
207 }
208 {
209 using V = std::variant<int, TMoveAssign>;
210 static_assert(std::is_trivially_move_assignable<V>::value, "");
211 }
212 {
213 using V = std::variant<int, TMoveAssignNTCopyAssign>;
214 static_assert(std::is_trivially_move_assignable<V>::value, "");
215 }
216 {
217 using V = std::variant<int, TrivialCopyNontrivialMove>;
218 static_assert(!std::is_trivially_move_assignable<V>::value, "");
219 }
220 {
221 using V = std::variant<int, CopyOnly>;
222 static_assert(std::is_trivially_move_assignable<V>::value, "");
223 }
224 #endif // > C++17
225 }
226
test_move_assignment_empty_empty()227 void test_move_assignment_empty_empty() {
228 #ifndef TEST_HAS_NO_EXCEPTIONS
229 using MET = MakeEmptyT;
230 {
231 using V = std::variant<int, long, MET>;
232 V v1(std::in_place_index<0>);
233 makeEmpty(v1);
234 V v2(std::in_place_index<0>);
235 makeEmpty(v2);
236 V &vref = (v1 = std::move(v2));
237 assert(&vref == &v1);
238 assert(v1.valueless_by_exception());
239 assert(v1.index() == std::variant_npos);
240 }
241 #endif // TEST_HAS_NO_EXCEPTIONS
242 }
243
test_move_assignment_non_empty_empty()244 void test_move_assignment_non_empty_empty() {
245 #ifndef TEST_HAS_NO_EXCEPTIONS
246 using MET = MakeEmptyT;
247 {
248 using V = std::variant<int, MET>;
249 V v1(std::in_place_index<0>, 42);
250 V v2(std::in_place_index<0>);
251 makeEmpty(v2);
252 V &vref = (v1 = std::move(v2));
253 assert(&vref == &v1);
254 assert(v1.valueless_by_exception());
255 assert(v1.index() == std::variant_npos);
256 }
257 {
258 using V = std::variant<int, MET, std::string>;
259 V v1(std::in_place_index<2>, "hello");
260 V v2(std::in_place_index<0>);
261 makeEmpty(v2);
262 V &vref = (v1 = std::move(v2));
263 assert(&vref == &v1);
264 assert(v1.valueless_by_exception());
265 assert(v1.index() == std::variant_npos);
266 }
267 #endif // TEST_HAS_NO_EXCEPTIONS
268 }
269
test_move_assignment_empty_non_empty()270 void test_move_assignment_empty_non_empty() {
271 #ifndef TEST_HAS_NO_EXCEPTIONS
272 using MET = MakeEmptyT;
273 {
274 using V = std::variant<int, MET>;
275 V v1(std::in_place_index<0>);
276 makeEmpty(v1);
277 V v2(std::in_place_index<0>, 42);
278 V &vref = (v1 = std::move(v2));
279 assert(&vref == &v1);
280 assert(v1.index() == 0);
281 assert(std::get<0>(v1) == 42);
282 }
283 {
284 using V = std::variant<int, MET, std::string>;
285 V v1(std::in_place_index<0>);
286 makeEmpty(v1);
287 V v2(std::in_place_type<std::string>, "hello");
288 V &vref = (v1 = std::move(v2));
289 assert(&vref == &v1);
290 assert(v1.index() == 2);
291 assert(std::get<2>(v1) == "hello");
292 }
293 #endif // TEST_HAS_NO_EXCEPTIONS
294 }
295
296 template <typename T> struct Result { size_t index; T value; };
297
test_move_assignment_same_index()298 void test_move_assignment_same_index() {
299 {
300 using V = std::variant<int>;
301 V v1(43);
302 V v2(42);
303 V &vref = (v1 = std::move(v2));
304 assert(&vref == &v1);
305 assert(v1.index() == 0);
306 assert(std::get<0>(v1) == 42);
307 }
308 {
309 using V = std::variant<int, long, unsigned>;
310 V v1(43l);
311 V v2(42l);
312 V &vref = (v1 = std::move(v2));
313 assert(&vref == &v1);
314 assert(v1.index() == 1);
315 assert(std::get<1>(v1) == 42);
316 }
317 {
318 using V = std::variant<int, MoveAssign, unsigned>;
319 V v1(std::in_place_type<MoveAssign>, 43);
320 V v2(std::in_place_type<MoveAssign>, 42);
321 MoveAssign::reset();
322 V &vref = (v1 = std::move(v2));
323 assert(&vref == &v1);
324 assert(v1.index() == 1);
325 assert(std::get<1>(v1).value == 42);
326 assert(MoveAssign::move_construct == 0);
327 assert(MoveAssign::move_assign == 1);
328 }
329 #ifndef TEST_HAS_NO_EXCEPTIONS
330 using MET = MakeEmptyT;
331 {
332 using V = std::variant<int, MET, std::string>;
333 V v1(std::in_place_type<MET>);
334 MET &mref = std::get<1>(v1);
335 V v2(std::in_place_type<MET>);
336 try {
337 v1 = std::move(v2);
338 assert(false);
339 } catch (...) {
340 }
341 assert(v1.index() == 1);
342 assert(&std::get<1>(v1) == &mref);
343 }
344 #endif // TEST_HAS_NO_EXCEPTIONS
345
346 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
347 #if TEST_STD_VER > 17
348 {
349 struct {
350 constexpr Result<int> operator()() const {
351 using V = std::variant<int>;
352 V v(43);
353 V v2(42);
354 v = std::move(v2);
355 return {v.index(), std::get<0>(v)};
356 }
357 } test;
358 constexpr auto result = test();
359 static_assert(result.index == 0, "");
360 static_assert(result.value == 42, "");
361 }
362 {
363 struct {
364 constexpr Result<long> operator()() const {
365 using V = std::variant<int, long, unsigned>;
366 V v(43l);
367 V v2(42l);
368 v = std::move(v2);
369 return {v.index(), std::get<1>(v)};
370 }
371 } test;
372 constexpr auto result = test();
373 static_assert(result.index == 1, "");
374 static_assert(result.value == 42l, "");
375 }
376 {
377 struct {
378 constexpr Result<int> operator()() const {
379 using V = std::variant<int, TMoveAssign, unsigned>;
380 V v(std::in_place_type<TMoveAssign>, 43);
381 V v2(std::in_place_type<TMoveAssign>, 42);
382 v = std::move(v2);
383 return {v.index(), std::get<1>(v).value};
384 }
385 } test;
386 constexpr auto result = test();
387 static_assert(result.index == 1, "");
388 static_assert(result.value == 42, "");
389 }
390 #endif // > C++17
391 }
392
test_move_assignment_different_index()393 void test_move_assignment_different_index() {
394 {
395 using V = std::variant<int, long, unsigned>;
396 V v1(43);
397 V v2(42l);
398 V &vref = (v1 = std::move(v2));
399 assert(&vref == &v1);
400 assert(v1.index() == 1);
401 assert(std::get<1>(v1) == 42);
402 }
403 {
404 using V = std::variant<int, MoveAssign, unsigned>;
405 V v1(std::in_place_type<unsigned>, 43u);
406 V v2(std::in_place_type<MoveAssign>, 42);
407 MoveAssign::reset();
408 V &vref = (v1 = std::move(v2));
409 assert(&vref == &v1);
410 assert(v1.index() == 1);
411 assert(std::get<1>(v1).value == 42);
412 assert(MoveAssign::move_construct == 1);
413 assert(MoveAssign::move_assign == 0);
414 }
415 #ifndef TEST_HAS_NO_EXCEPTIONS
416 using MET = MakeEmptyT;
417 {
418 using V = std::variant<int, MET, std::string>;
419 V v1(std::in_place_type<int>);
420 V v2(std::in_place_type<MET>);
421 try {
422 v1 = std::move(v2);
423 assert(false);
424 } catch (...) {
425 }
426 assert(v1.valueless_by_exception());
427 assert(v1.index() == std::variant_npos);
428 }
429 {
430 using V = std::variant<int, MET, std::string>;
431 V v1(std::in_place_type<MET>);
432 V v2(std::in_place_type<std::string>, "hello");
433 V &vref = (v1 = std::move(v2));
434 assert(&vref == &v1);
435 assert(v1.index() == 2);
436 assert(std::get<2>(v1) == "hello");
437 }
438 #endif // TEST_HAS_NO_EXCEPTIONS
439
440 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
441 #if TEST_STD_VER > 17
442 {
443 struct {
444 constexpr Result<long> operator()() const {
445 using V = std::variant<int, long, unsigned>;
446 V v(43);
447 V v2(42l);
448 v = std::move(v2);
449 return {v.index(), std::get<1>(v)};
450 }
451 } test;
452 constexpr auto result = test();
453 static_assert(result.index == 1, "");
454 static_assert(result.value == 42l, "");
455 }
456 {
457 struct {
458 constexpr Result<long> operator()() const {
459 using V = std::variant<int, TMoveAssign, unsigned>;
460 V v(std::in_place_type<unsigned>, 43u);
461 V v2(std::in_place_type<TMoveAssign>, 42);
462 v = std::move(v2);
463 return {v.index(), std::get<1>(v).value};
464 }
465 } test;
466 constexpr auto result = test();
467 static_assert(result.index == 1, "");
468 static_assert(result.value == 42, "");
469 }
470 #endif // > C++17
471 }
472
473 template <size_t NewIdx, class ValueType>
test_constexpr_assign_imp(std::variant<long,void *,int> && v,ValueType && new_value)474 constexpr bool test_constexpr_assign_imp(
475 std::variant<long, void*, int>&& v, ValueType&& new_value)
476 {
477 std::variant<long, void*, int> v2(
478 std::forward<ValueType>(new_value));
479 const auto cp = v2;
480 v = std::move(v2);
481 return v.index() == NewIdx &&
482 std::get<NewIdx>(v) == std::get<NewIdx>(cp);
483 }
484
test_constexpr_move_assignment()485 void test_constexpr_move_assignment() {
486 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
487 #if TEST_STD_VER > 17
488 using V = std::variant<long, void*, int>;
489 static_assert(std::is_trivially_copyable<V>::value, "");
490 static_assert(std::is_trivially_move_assignable<V>::value, "");
491 static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
492 static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
493 static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
494 static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
495 #endif // > C++17
496 }
497
main(int,char **)498 int main(int, char**) {
499 test_move_assignment_empty_empty();
500 test_move_assignment_non_empty_empty();
501 test_move_assignment_empty_non_empty();
502 test_move_assignment_same_index();
503 test_move_assignment_different_index();
504 test_move_assignment_sfinae();
505 test_move_assignment_noexcept();
506 test_constexpr_move_assignment();
507
508 return 0;
509 }
510