[gn build] Port 0ee037b861f9
[llvm-project.git] / libcxx / test / std / utilities / variant / variant.variant / variant.assign / move.pass.cpp
blob157ff68f374825e81e1852220b575ad604a41dfc
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 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14
11 // <variant>
13 // template <class ...Types> class variant;
15 // constexpr variant& operator=(variant&&) noexcept(see below);
17 #include <cassert>
18 #include <string>
19 #include <type_traits>
20 #include <utility>
21 #include <variant>
23 #include "test_macros.h"
24 #include "variant_test_helpers.h"
26 struct NoCopy {
27 NoCopy(const NoCopy&) = delete;
28 NoCopy& operator=(const NoCopy&) = default;
31 struct CopyOnly {
32 CopyOnly(const CopyOnly&) = default;
33 CopyOnly(CopyOnly&&) = delete;
34 CopyOnly& operator=(const CopyOnly&) = default;
35 CopyOnly& operator=(CopyOnly&&) = delete;
38 struct MoveOnly {
39 MoveOnly(const MoveOnly&) = delete;
40 MoveOnly(MoveOnly&&) = default;
41 MoveOnly& operator=(const MoveOnly&) = delete;
42 MoveOnly& operator=(MoveOnly&&) = default;
45 struct MoveOnlyNT {
46 MoveOnlyNT(const MoveOnlyNT&) = delete;
47 MoveOnlyNT(MoveOnlyNT&&) {}
48 MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;
49 MoveOnlyNT& operator=(MoveOnlyNT&&) = default;
52 struct MoveOnlyOddNothrow {
53 MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}
54 MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete;
55 MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;
56 MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete;
59 struct MoveAssignOnly {
60 MoveAssignOnly(MoveAssignOnly&&) = delete;
61 MoveAssignOnly& operator=(MoveAssignOnly&&) = default;
64 struct MoveAssign {
65 constexpr MoveAssign(int v, int* move_ctor, int* move_assi)
66 : value(v), move_construct(move_ctor), move_assign(move_assi) {}
67 constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) {
68 ++*move_construct;
69 o.value = -1;
71 constexpr MoveAssign& operator=(MoveAssign&& o) {
72 value = o.value;
73 move_construct = o.move_construct;
74 move_assign = o.move_assign;
75 ++*move_assign;
76 o.value = -1;
77 return *this;
79 int value;
80 int* move_construct;
81 int* move_assign;
84 struct NTMoveAssign {
85 constexpr NTMoveAssign(int v) : value(v) {}
86 NTMoveAssign(const NTMoveAssign&) = default;
87 NTMoveAssign(NTMoveAssign&&) = default;
88 NTMoveAssign& operator=(const NTMoveAssign& that) = default;
89 NTMoveAssign& operator=(NTMoveAssign&& that) {
90 value = that.value;
91 that.value = -1;
92 return *this;
94 int value;
97 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
98 static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
100 struct TMoveAssign {
101 constexpr TMoveAssign(int v) : value(v) {}
102 TMoveAssign(const TMoveAssign&) = delete;
103 TMoveAssign(TMoveAssign&&) = default;
104 TMoveAssign& operator=(const TMoveAssign&) = delete;
105 TMoveAssign& operator=(TMoveAssign&&) = default;
106 int value;
109 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
111 struct TMoveAssignNTCopyAssign {
112 constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
113 TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;
114 TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default;
115 TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {
116 value = that.value;
117 return *this;
119 TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;
120 int value;
123 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
125 struct TrivialCopyNontrivialMove {
126 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
127 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
128 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
129 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }
132 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
133 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
135 constexpr void test_move_assignment_noexcept() {
137 using V = std::variant<int>;
138 static_assert(std::is_nothrow_move_assignable<V>::value, "");
141 using V = std::variant<MoveOnly>;
142 static_assert(std::is_nothrow_move_assignable<V>::value, "");
145 using V = std::variant<int, long>;
146 static_assert(std::is_nothrow_move_assignable<V>::value, "");
149 using V = std::variant<int, MoveOnly>;
150 static_assert(std::is_nothrow_move_assignable<V>::value, "");
153 using V = std::variant<MoveOnlyNT>;
154 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
157 using V = std::variant<MoveOnlyOddNothrow>;
158 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
162 constexpr void test_move_assignment_sfinae() {
164 using V = std::variant<int, long>;
165 static_assert(std::is_move_assignable<V>::value, "");
168 using V = std::variant<int, CopyOnly>;
169 static_assert(std::is_move_assignable<V>::value, "");
172 using V = std::variant<int, NoCopy>;
173 static_assert(!std::is_move_assignable<V>::value, "");
176 using V = std::variant<int, MoveOnly>;
177 static_assert(std::is_move_assignable<V>::value, "");
180 using V = std::variant<int, MoveOnlyNT>;
181 static_assert(std::is_move_assignable<V>::value, "");
184 // variant only provides move assignment when the types also provide
185 // a move constructor.
186 using V = std::variant<int, MoveAssignOnly>;
187 static_assert(!std::is_move_assignable<V>::value, "");
190 // Make sure we properly propagate triviality (see P0602R4).
192 using V = std::variant<int, long>;
193 static_assert(std::is_trivially_move_assignable<V>::value, "");
196 using V = std::variant<int, NTMoveAssign>;
197 static_assert(!std::is_trivially_move_assignable<V>::value, "");
198 static_assert(std::is_move_assignable<V>::value, "");
201 using V = std::variant<int, TMoveAssign>;
202 static_assert(std::is_trivially_move_assignable<V>::value, "");
205 using V = std::variant<int, TMoveAssignNTCopyAssign>;
206 static_assert(std::is_trivially_move_assignable<V>::value, "");
209 using V = std::variant<int, TrivialCopyNontrivialMove>;
210 static_assert(!std::is_trivially_move_assignable<V>::value, "");
213 using V = std::variant<int, CopyOnly>;
214 static_assert(std::is_trivially_move_assignable<V>::value, "");
218 void test_move_assignment_empty_empty() {
219 #ifndef TEST_HAS_NO_EXCEPTIONS
220 using MET = MakeEmptyT;
222 using V = std::variant<int, long, MET>;
223 V v1(std::in_place_index<0>);
224 makeEmpty(v1);
225 V v2(std::in_place_index<0>);
226 makeEmpty(v2);
227 V& vref = (v1 = std::move(v2));
228 assert(&vref == &v1);
229 assert(v1.valueless_by_exception());
230 assert(v1.index() == std::variant_npos);
232 #endif // TEST_HAS_NO_EXCEPTIONS
235 void test_move_assignment_non_empty_empty() {
236 #ifndef TEST_HAS_NO_EXCEPTIONS
237 using MET = MakeEmptyT;
239 using V = std::variant<int, MET>;
240 V v1(std::in_place_index<0>, 42);
241 V v2(std::in_place_index<0>);
242 makeEmpty(v2);
243 V& vref = (v1 = std::move(v2));
244 assert(&vref == &v1);
245 assert(v1.valueless_by_exception());
246 assert(v1.index() == std::variant_npos);
249 using V = std::variant<int, MET, std::string>;
250 V v1(std::in_place_index<2>, "hello");
251 V v2(std::in_place_index<0>);
252 makeEmpty(v2);
253 V& vref = (v1 = std::move(v2));
254 assert(&vref == &v1);
255 assert(v1.valueless_by_exception());
256 assert(v1.index() == std::variant_npos);
258 #endif // TEST_HAS_NO_EXCEPTIONS
261 void test_move_assignment_empty_non_empty() {
262 #ifndef TEST_HAS_NO_EXCEPTIONS
263 using MET = MakeEmptyT;
265 using V = std::variant<int, MET>;
266 V v1(std::in_place_index<0>);
267 makeEmpty(v1);
268 V v2(std::in_place_index<0>, 42);
269 V& vref = (v1 = std::move(v2));
270 assert(&vref == &v1);
271 assert(v1.index() == 0);
272 assert(std::get<0>(v1) == 42);
275 using V = std::variant<int, MET, std::string>;
276 V v1(std::in_place_index<0>);
277 makeEmpty(v1);
278 V v2(std::in_place_type<std::string>, "hello");
279 V& vref = (v1 = std::move(v2));
280 assert(&vref == &v1);
281 assert(v1.index() == 2);
282 assert(std::get<2>(v1) == "hello");
284 #endif // TEST_HAS_NO_EXCEPTIONS
287 template <typename T>
288 struct Result {
289 std::size_t index;
290 T value;
293 TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() {
295 using V = std::variant<int>;
296 V v1(43);
297 V v2(42);
298 V& vref = (v1 = std::move(v2));
299 assert(&vref == &v1);
300 assert(v1.index() == 0);
301 assert(std::get<0>(v1) == 42);
304 using V = std::variant<int, long, unsigned>;
305 V v1(43l);
306 V v2(42l);
307 V& vref = (v1 = std::move(v2));
308 assert(&vref == &v1);
309 assert(v1.index() == 1);
310 assert(std::get<1>(v1) == 42);
313 using V = std::variant<int, MoveAssign, unsigned>;
314 int move_construct = 0;
315 int move_assign = 0;
316 V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);
317 V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
318 V& vref = (v1 = std::move(v2));
319 assert(&vref == &v1);
320 assert(v1.index() == 1);
321 assert(std::get<1>(v1).value == 42);
322 assert(move_construct == 0);
323 assert(move_assign == 1);
326 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
328 struct {
329 constexpr Result<int> operator()() const {
330 using V = std::variant<int>;
331 V v(43);
332 V v2(42);
333 v = std::move(v2);
334 return {v.index(), std::get<0>(v)};
336 } test;
337 constexpr auto result = test();
338 static_assert(result.index == 0, "");
339 static_assert(result.value == 42, "");
342 struct {
343 constexpr Result<long> operator()() const {
344 using V = std::variant<int, long, unsigned>;
345 V v(43l);
346 V v2(42l);
347 v = std::move(v2);
348 return {v.index(), std::get<1>(v)};
350 } test;
351 constexpr auto result = test();
352 static_assert(result.index == 1, "");
353 static_assert(result.value == 42l, "");
356 struct {
357 constexpr Result<int> operator()() const {
358 using V = std::variant<int, TMoveAssign, unsigned>;
359 V v(std::in_place_type<TMoveAssign>, 43);
360 V v2(std::in_place_type<TMoveAssign>, 42);
361 v = std::move(v2);
362 return {v.index(), std::get<1>(v).value};
364 } test;
365 constexpr auto result = test();
366 static_assert(result.index == 1, "");
367 static_assert(result.value == 42, "");
371 TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() {
373 using V = std::variant<int, long, unsigned>;
374 V v1(43);
375 V v2(42l);
376 V& vref = (v1 = std::move(v2));
377 assert(&vref == &v1);
378 assert(v1.index() == 1);
379 assert(std::get<1>(v1) == 42);
382 using V = std::variant<int, MoveAssign, unsigned>;
383 int move_construct = 0;
384 int move_assign = 0;
385 V v1(std::in_place_type<unsigned>, 43u);
386 V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
387 V& vref = (v1 = std::move(v2));
388 assert(&vref == &v1);
389 assert(v1.index() == 1);
390 assert(std::get<1>(v1).value == 42);
391 assert(move_construct == 1);
392 assert(move_assign == 0);
395 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
397 struct {
398 constexpr Result<long> operator()() const {
399 using V = std::variant<int, long, unsigned>;
400 V v(43);
401 V v2(42l);
402 v = std::move(v2);
403 return {v.index(), std::get<1>(v)};
405 } test;
406 constexpr auto result = test();
407 static_assert(result.index == 1, "");
408 static_assert(result.value == 42l, "");
411 struct {
412 constexpr Result<long> operator()() const {
413 using V = std::variant<int, TMoveAssign, unsigned>;
414 V v(std::in_place_type<unsigned>, 43u);
415 V v2(std::in_place_type<TMoveAssign>, 42);
416 v = std::move(v2);
417 return {v.index(), std::get<1>(v).value};
419 } test;
420 constexpr auto result = test();
421 static_assert(result.index == 1, "");
422 static_assert(result.value == 42, "");
426 void test_assignment_throw() {
427 #ifndef TEST_HAS_NO_EXCEPTIONS
428 using MET = MakeEmptyT;
429 // same index
431 using V = std::variant<int, MET, std::string>;
432 V v1(std::in_place_type<MET>);
433 MET& mref = std::get<1>(v1);
434 V v2(std::in_place_type<MET>);
435 try {
436 v1 = std::move(v2);
437 assert(false);
438 } catch (...) {
440 assert(v1.index() == 1);
441 assert(&std::get<1>(v1) == &mref);
444 // different indices
446 using V = std::variant<int, MET, std::string>;
447 V v1(std::in_place_type<int>);
448 V v2(std::in_place_type<MET>);
449 try {
450 v1 = std::move(v2);
451 assert(false);
452 } catch (...) {
454 assert(v1.valueless_by_exception());
455 assert(v1.index() == std::variant_npos);
458 using V = std::variant<int, MET, std::string>;
459 V v1(std::in_place_type<MET>);
460 V v2(std::in_place_type<std::string>, "hello");
461 V& vref = (v1 = std::move(v2));
462 assert(&vref == &v1);
463 assert(v1.index() == 2);
464 assert(std::get<2>(v1) == "hello");
466 #endif // TEST_HAS_NO_EXCEPTIONS
469 template <std::size_t NewIdx, class T, class ValueType>
470 constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
471 using Variant = std::decay_t<T>;
472 Variant v2(std::forward<ValueType>(new_value));
473 const auto cp = v2;
474 v = std::move(v2);
475 assert(v.index() == NewIdx);
476 assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));
479 constexpr void test_constexpr_move_assignment_trivial() {
480 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
481 using V = std::variant<long, void*, int>;
482 static_assert(std::is_trivially_copyable<V>::value, "");
483 static_assert(std::is_trivially_move_assignable<V>::value, "");
484 test_constexpr_assign_imp<0>(V(42l), 101l);
485 test_constexpr_assign_imp<0>(V(nullptr), 101l);
486 test_constexpr_assign_imp<1>(V(42l), nullptr);
487 test_constexpr_assign_imp<2>(V(42l), 101);
490 struct NonTrivialMoveAssign {
491 int i = 0;
492 constexpr NonTrivialMoveAssign(int ii) : i(ii) {}
493 constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;
494 constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}
495 constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;
496 constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {
497 i = o.i;
498 return *this;
500 TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;
501 friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }
504 TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() {
505 using V = std::variant<long, void*, NonTrivialMoveAssign>;
506 static_assert(!std::is_trivially_copyable<V>::value);
507 static_assert(!std::is_trivially_move_assignable<V>::value);
508 test_constexpr_assign_imp<0>(V(42l), 101l);
509 test_constexpr_assign_imp<0>(V(nullptr), 101l);
510 test_constexpr_assign_imp<1>(V(42l), nullptr);
511 test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5));
512 test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5));
515 void non_constexpr_test() {
516 test_move_assignment_empty_empty();
517 test_move_assignment_non_empty_empty();
518 test_move_assignment_empty_non_empty();
519 test_assignment_throw();
522 constexpr bool cxx17_constexpr_test() {
523 test_move_assignment_sfinae();
524 test_move_assignment_noexcept();
525 test_constexpr_move_assignment_trivial();
527 return true;
530 TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
531 test_move_assignment_same_index();
532 test_move_assignment_different_index();
533 test_constexpr_move_assignment_non_trivial();
535 return true;
538 int main(int, char**) {
539 non_constexpr_test();
540 cxx17_constexpr_test();
541 cxx20_constexpr_test();
543 static_assert(cxx17_constexpr_test());
544 #if TEST_STD_VER >= 20
545 static_assert(cxx20_constexpr_test());
546 #endif
547 return 0;