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// <variant>
12
13// template <class ...Types> class variant;
14
15// constexpr variant& operator=(variant const&);
16
17#include <cassert>
18#include <string>
19#include <type_traits>
20#include <variant>
21
22#include "test_macros.h"
23
24struct NoCopy {
25 NoCopy(const NoCopy&) = delete;
26 NoCopy& operator=(const NoCopy&) = default;
27};
28
29struct CopyOnly {
30 CopyOnly(const CopyOnly&) = default;
31 CopyOnly(CopyOnly&&) = delete;
32 CopyOnly& operator=(const CopyOnly&) = default;
33 CopyOnly& operator=(CopyOnly&&) = delete;
34};
35
36struct MoveOnly {
37 MoveOnly(const MoveOnly&) = delete;
38 MoveOnly(MoveOnly&&) = default;
39 MoveOnly& operator=(const MoveOnly&) = default;
40};
41
42struct MoveOnlyNT {
43 MoveOnlyNT(const MoveOnlyNT&) = delete;
44 MoveOnlyNT(MoveOnlyNT&&) {}
45 MoveOnlyNT& operator=(const MoveOnlyNT&) = default;
46};
47
48struct CopyAssign {
49 constexpr CopyAssign(int v, int* alv, int* cpy_ctr, int* cpy_assi, int* move_ctr, int* move_assi)
50 : value(v),
51 alive(alv),
52 copy_construct(cpy_ctr),
53 copy_assign(cpy_assi),
54 move_construct(move_ctr),
55 move_assign(move_assi) {
56 ++*alive;
57 }
58 constexpr CopyAssign(const CopyAssign& o)
59 : value(o.value),
60 alive(o.alive),
61 copy_construct(o.copy_construct),
62 copy_assign(o.copy_assign),
63 move_construct(o.move_construct),
64 move_assign(o.move_assign) {
65 ++*alive;
66 ++*copy_construct;
67 }
68 constexpr CopyAssign(CopyAssign&& o) noexcept
69 : value(o.value),
70 alive(o.alive),
71 copy_construct(o.copy_construct),
72 copy_assign(o.copy_assign),
73 move_construct(o.move_construct),
74 move_assign(o.move_assign) {
75 o.value = -1;
76 ++*alive;
77 ++*move_construct;
78 }
79 constexpr CopyAssign& operator=(const CopyAssign& o) {
80 value = o.value;
81 alive = o.alive;
82 copy_construct = o.copy_construct;
83 copy_assign = o.copy_assign;
84 move_construct = o.move_construct;
85 move_assign = o.move_assign;
86 ++*copy_assign;
87 return *this;
88 }
89 constexpr CopyAssign& operator=(CopyAssign&& o) noexcept {
90 value = o.value;
91 alive = o.alive;
92 copy_construct = o.copy_construct;
93 copy_assign = o.copy_assign;
94 move_construct = o.move_construct;
95 move_assign = o.move_assign;
96 o.value = -1;
97 ++*move_assign;
98 return *this;
99 }
100 TEST_CONSTEXPR_CXX20 ~CopyAssign() { --*alive; }
101 int value;
102 int* alive;
103 int* copy_construct;
104 int* copy_assign;
105 int* move_construct;
106 int* move_assign;
107};
108
109struct CopyMaybeThrows {
110 CopyMaybeThrows(const CopyMaybeThrows&);
111 CopyMaybeThrows& operator=(const CopyMaybeThrows&);
112};
113struct CopyDoesThrow {
114 CopyDoesThrow(const CopyDoesThrow&) noexcept(false);
115 CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false);
116};
117
118struct NTCopyAssign {
119 constexpr NTCopyAssign(int v) : value(v) {}
120 NTCopyAssign(const NTCopyAssign&) = default;
121 NTCopyAssign(NTCopyAssign&&) = default;
122 NTCopyAssign& operator=(const NTCopyAssign& that) {
123 value = that.value;
124 return *this;
125 };
126 NTCopyAssign& operator=(NTCopyAssign&&) = delete;
127 int value;
128};
129
130static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");
131static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
132
133struct TCopyAssign {
134 constexpr TCopyAssign(int v) : value(v) {}
135 TCopyAssign(const TCopyAssign&) = default;
136 TCopyAssign(TCopyAssign&&) = default;
137 TCopyAssign& operator=(const TCopyAssign&) = default;
138 TCopyAssign& operator=(TCopyAssign&&) = delete;
139 int value;
140};
141
142static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
143
144struct TCopyAssignNTMoveAssign {
145 constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
146 TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&) = default;
147 TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&) = default;
148 TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default;
149 TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) {
150 value = that.value;
151 that.value = -1;
152 return *this;
153 }
154 int value;
155};
156
157static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
158
159#ifndef TEST_HAS_NO_EXCEPTIONS
160struct CopyThrows {
161 CopyThrows() = default;
162 CopyThrows(const CopyThrows&) { throw 42; }
163 CopyThrows& operator=(const CopyThrows&) { throw 42; }
164};
165
166struct CopyCannotThrow {
167 static int alive;
168 CopyCannotThrow() { ++alive; }
169 CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; }
170 CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); }
171 CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default;
172 CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept {
173 assert(false);
174 return *this;
175 }
176};
177
178int CopyCannotThrow::alive = 0;
179
180struct MoveThrows {
181 static int alive;
182 MoveThrows() { ++alive; }
183 MoveThrows(const MoveThrows&) { ++alive; }
184 MoveThrows(MoveThrows&&) { throw 42; }
185 MoveThrows& operator=(const MoveThrows&) { return *this; }
186 MoveThrows& operator=(MoveThrows&&) { throw 42; }
187 ~MoveThrows() { --alive; }
188};
189
190int MoveThrows::alive = 0;
191
192struct MakeEmptyT {
193 static int alive;
194 MakeEmptyT() { ++alive; }
195 MakeEmptyT(const MakeEmptyT&) {
196 ++alive;
197 // Don't throw from the copy constructor since variant's assignment
198 // operator performs a copy before committing to the assignment.
199 }
200 MakeEmptyT(MakeEmptyT&&) { throw 42; }
201 MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
202 MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
203 ~MakeEmptyT() { --alive; }
204};
205
206int MakeEmptyT::alive = 0;
207
208template <class Variant>
209void makeEmpty(Variant& v) {
210 Variant v2(std::in_place_type<MakeEmptyT>);
211 try {
212 v = std::move(v2);
213 assert(false);
214 } catch (...) {
215 assert(v.valueless_by_exception());
216 }
217}
218#endif // TEST_HAS_NO_EXCEPTIONS
219
220constexpr void test_copy_assignment_not_noexcept() {
221 {
222 using V = std::variant<CopyMaybeThrows>;
223 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
224 }
225 {
226 using V = std::variant<int, CopyDoesThrow>;
227 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
228 }
229}
230
231constexpr void test_copy_assignment_sfinae() {
232 {
233 using V = std::variant<int, long>;
234 static_assert(std::is_copy_assignable<V>::value, "");
235 }
236 {
237 using V = std::variant<int, CopyOnly>;
238 static_assert(std::is_copy_assignable<V>::value, "");
239 }
240 {
241 using V = std::variant<int, NoCopy>;
242 static_assert(!std::is_copy_assignable<V>::value, "");
243 }
244 {
245 using V = std::variant<int, MoveOnly>;
246 static_assert(!std::is_copy_assignable<V>::value, "");
247 }
248 {
249 using V = std::variant<int, MoveOnlyNT>;
250 static_assert(!std::is_copy_assignable<V>::value, "");
251 }
252
253 // Make sure we properly propagate triviality (see P0602R4).
254 {
255 using V = std::variant<int, long>;
256 static_assert(std::is_trivially_copy_assignable<V>::value, "");
257 }
258 {
259 using V = std::variant<int, NTCopyAssign>;
260 static_assert(!std::is_trivially_copy_assignable<V>::value, "");
261 static_assert(std::is_copy_assignable<V>::value, "");
262 }
263 {
264 using V = std::variant<int, TCopyAssign>;
265 static_assert(std::is_trivially_copy_assignable<V>::value, "");
266 }
267 {
268 using V = std::variant<int, TCopyAssignNTMoveAssign>;
269 static_assert(std::is_trivially_copy_assignable<V>::value, "");
270 }
271 {
272 using V = std::variant<int, CopyOnly>;
273 static_assert(std::is_trivially_copy_assignable<V>::value, "");
274 }
275}
276
277void test_copy_assignment_empty_empty() {
278#ifndef TEST_HAS_NO_EXCEPTIONS
279 using MET = MakeEmptyT;
280 {
281 using V = std::variant<int, long, MET>;
282 V v1(std::in_place_index<0>);
283 makeEmpty(v&: v1);
284 V v2(std::in_place_index<0>);
285 makeEmpty(v&: v2);
286 V& vref = (v1 = v2);
287 assert(&vref == &v1);
288 assert(v1.valueless_by_exception());
289 assert(v1.index() == std::variant_npos);
290 }
291#endif // TEST_HAS_NO_EXCEPTIONS
292}
293
294void test_copy_assignment_non_empty_empty() {
295#ifndef TEST_HAS_NO_EXCEPTIONS
296 using MET = MakeEmptyT;
297 {
298 using V = std::variant<int, MET>;
299 V v1(std::in_place_index<0>, 42);
300 V v2(std::in_place_index<0>);
301 makeEmpty(v&: v2);
302 V& vref = (v1 = v2);
303 assert(&vref == &v1);
304 assert(v1.valueless_by_exception());
305 assert(v1.index() == std::variant_npos);
306 }
307 {
308 using V = std::variant<int, MET, std::string>;
309 V v1(std::in_place_index<2>, "hello");
310 V v2(std::in_place_index<0>);
311 makeEmpty(v&: v2);
312 V& vref = (v1 = v2);
313 assert(&vref == &v1);
314 assert(v1.valueless_by_exception());
315 assert(v1.index() == std::variant_npos);
316 }
317#endif // TEST_HAS_NO_EXCEPTIONS
318}
319
320void test_copy_assignment_empty_non_empty() {
321#ifndef TEST_HAS_NO_EXCEPTIONS
322 using MET = MakeEmptyT;
323 {
324 using V = std::variant<int, MET>;
325 V v1(std::in_place_index<0>);
326 makeEmpty(v&: v1);
327 V v2(std::in_place_index<0>, 42);
328 V& vref = (v1 = v2);
329 assert(&vref == &v1);
330 assert(v1.index() == 0);
331 assert(std::get<0>(v1) == 42);
332 }
333 {
334 using V = std::variant<int, MET, std::string>;
335 V v1(std::in_place_index<0>);
336 makeEmpty(v&: v1);
337 V v2(std::in_place_type<std::string>, "hello");
338 V& vref = (v1 = v2);
339 assert(&vref == &v1);
340 assert(v1.index() == 2);
341 assert(std::get<2>(v1) == "hello");
342 }
343#endif // TEST_HAS_NO_EXCEPTIONS
344}
345
346template <typename T>
347struct Result {
348 std::size_t index;
349 T value;
350};
351
352TEST_CONSTEXPR_CXX20 void test_copy_assignment_same_index() {
353 {
354 using V = std::variant<int>;
355 V v1(43);
356 V v2(42);
357 V& vref = (v1 = v2);
358 assert(&vref == &v1);
359 assert(v1.index() == 0);
360 assert(std::get<0>(v1) == 42);
361 }
362 {
363 using V = std::variant<int, long, unsigned>;
364 V v1(43l);
365 V v2(42l);
366 V& vref = (v1 = v2);
367 assert(&vref == &v1);
368 assert(v1.index() == 1);
369 assert(std::get<1>(v1) == 42);
370 }
371 {
372 using V = std::variant<int, CopyAssign, unsigned>;
373 int alive = 0;
374 int copy_construct = 0;
375 int copy_assign = 0;
376 int move_construct = 0;
377 int move_assign = 0;
378 V v1(std::in_place_type<CopyAssign>, 43, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);
379 V v2(std::in_place_type<CopyAssign>, 42, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);
380 V& vref = (v1 = v2);
381 assert(&vref == &v1);
382 assert(v1.index() == 1);
383 assert(std::get<1>(v1).value == 42);
384 assert(copy_construct == 0);
385 assert(move_construct == 0);
386 assert(copy_assign == 1);
387 }
388
389 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
390 {
391 struct {
392 constexpr Result<int> operator()() const {
393 using V = std::variant<int>;
394 V v(43);
395 V v2(42);
396 v = v2;
397 return {.index: v.index(), .value: std::get<0>(v&: v)};
398 }
399 } test;
400 constexpr auto result = test();
401 static_assert(result.index == 0, "");
402 static_assert(result.value == 42, "");
403 }
404 {
405 struct {
406 constexpr Result<long> operator()() const {
407 using V = std::variant<int, long, unsigned>;
408 V v(43l);
409 V v2(42l);
410 v = v2;
411 return {.index: v.index(), .value: std::get<1>(v&: v)};
412 }
413 } test;
414 constexpr auto result = test();
415 static_assert(result.index == 1, "");
416 static_assert(result.value == 42l, "");
417 }
418 {
419 struct {
420 constexpr Result<int> operator()() const {
421 using V = std::variant<int, TCopyAssign, unsigned>;
422 V v(std::in_place_type<TCopyAssign>, 43);
423 V v2(std::in_place_type<TCopyAssign>, 42);
424 v = v2;
425 return {.index: v.index(), .value: std::get<1>(v&: v).value};
426 }
427 } test;
428 constexpr auto result = test();
429 static_assert(result.index == 1, "");
430 static_assert(result.value == 42, "");
431 }
432 {
433 struct {
434 constexpr Result<int> operator()() const {
435 using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;
436 V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);
437 V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);
438 v = v2;
439 return {.index: v.index(), .value: std::get<1>(v&: v).value};
440 }
441 } test;
442 constexpr auto result = test();
443 static_assert(result.index == 1, "");
444 static_assert(result.value == 42, "");
445 }
446}
447
448TEST_CONSTEXPR_CXX20 void test_copy_assignment_different_index() {
449 {
450 using V = std::variant<int, long, unsigned>;
451 V v1(43);
452 V v2(42l);
453 V& vref = (v1 = v2);
454 assert(&vref == &v1);
455 assert(v1.index() == 1);
456 assert(std::get<1>(v1) == 42);
457 }
458 {
459 using V = std::variant<int, CopyAssign, unsigned>;
460 int alive = 0;
461 int copy_construct = 0;
462 int copy_assign = 0;
463 int move_construct = 0;
464 int move_assign = 0;
465 V v1(std::in_place_type<unsigned>, 43u);
466 V v2(std::in_place_type<CopyAssign>, 42, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);
467 assert(copy_construct == 0);
468 assert(move_construct == 0);
469 assert(alive == 1);
470 V& vref = (v1 = v2);
471 assert(&vref == &v1);
472 assert(v1.index() == 1);
473 assert(std::get<1>(v1).value == 42);
474 assert(alive == 2);
475 assert(copy_construct == 1);
476 assert(move_construct == 1);
477 assert(copy_assign == 0);
478 }
479
480 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
481 {
482 struct {
483 constexpr Result<long> operator()() const {
484 using V = std::variant<int, long, unsigned>;
485 V v(43);
486 V v2(42l);
487 v = v2;
488 return {.index: v.index(), .value: std::get<1>(v&: v)};
489 }
490 } test;
491 constexpr auto result = test();
492 static_assert(result.index == 1, "");
493 static_assert(result.value == 42l, "");
494 }
495 {
496 struct {
497 constexpr Result<int> operator()() const {
498 using V = std::variant<int, TCopyAssign, unsigned>;
499 V v(std::in_place_type<unsigned>, 43u);
500 V v2(std::in_place_type<TCopyAssign>, 42);
501 v = v2;
502 return {.index: v.index(), .value: std::get<1>(v&: v).value};
503 }
504 } test;
505 constexpr auto result = test();
506 static_assert(result.index == 1, "");
507 static_assert(result.value == 42, "");
508 }
509}
510
511void test_assignment_throw() {
512#ifndef TEST_HAS_NO_EXCEPTIONS
513 using MET = MakeEmptyT;
514 // same index
515 {
516 using V = std::variant<int, MET, std::string>;
517 V v1(std::in_place_type<MET>);
518 MET& mref = std::get<1>(v&: v1);
519 V v2(std::in_place_type<MET>);
520 try {
521 v1 = v2;
522 assert(false);
523 } catch (...) {
524 }
525 assert(v1.index() == 1);
526 assert(&std::get<1>(v1) == &mref);
527 }
528
529 // difference indices
530 {
531 using V = std::variant<int, CopyThrows, std::string>;
532 V v1(std::in_place_type<std::string>, "hello");
533 V v2(std::in_place_type<CopyThrows>);
534 try {
535 v1 = v2;
536 assert(false);
537 } catch (...) { /* ... */
538 }
539 // Test that copy construction is used directly if move construction may throw,
540 // resulting in a valueless variant if copy throws.
541 assert(v1.valueless_by_exception());
542 }
543 {
544 using V = std::variant<int, MoveThrows, std::string>;
545 V v1(std::in_place_type<std::string>, "hello");
546 V v2(std::in_place_type<MoveThrows>);
547 assert(MoveThrows::alive == 1);
548 // Test that copy construction is used directly if move construction may throw.
549 v1 = v2;
550 assert(v1.index() == 1);
551 assert(v2.index() == 1);
552 assert(MoveThrows::alive == 2);
553 }
554 {
555 // Test that direct copy construction is preferred when it cannot throw.
556 using V = std::variant<int, CopyCannotThrow, std::string>;
557 V v1(std::in_place_type<std::string>, "hello");
558 V v2(std::in_place_type<CopyCannotThrow>);
559 assert(CopyCannotThrow::alive == 1);
560 v1 = v2;
561 assert(v1.index() == 1);
562 assert(v2.index() == 1);
563 assert(CopyCannotThrow::alive == 2);
564 }
565 {
566 using V = std::variant<int, CopyThrows, std::string>;
567 V v1(std::in_place_type<CopyThrows>);
568 V v2(std::in_place_type<std::string>, "hello");
569 V& vref = (v1 = v2);
570 assert(&vref == &v1);
571 assert(v1.index() == 2);
572 assert(std::get<2>(v1) == "hello");
573 assert(v2.index() == 2);
574 assert(std::get<2>(v2) == "hello");
575 }
576 {
577 using V = std::variant<int, MoveThrows, std::string>;
578 V v1(std::in_place_type<MoveThrows>);
579 V v2(std::in_place_type<std::string>, "hello");
580 V& vref = (v1 = v2);
581 assert(&vref == &v1);
582 assert(v1.index() == 2);
583 assert(std::get<2>(v1) == "hello");
584 assert(v2.index() == 2);
585 assert(std::get<2>(v2) == "hello");
586 }
587#endif // TEST_HAS_NO_EXCEPTIONS
588}
589
590template <std::size_t NewIdx, class T, class ValueType>
591constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
592 using Variant = std::decay_t<T>;
593 const Variant cp(std::forward<ValueType>(new_value));
594 v = cp;
595 assert(v.index() == NewIdx);
596 assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));
597}
598
599constexpr void test_constexpr_copy_assignment_trivial() {
600 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
601 using V = std::variant<long, void*, int>;
602 static_assert(std::is_trivially_copyable<V>::value, "");
603 static_assert(std::is_trivially_copy_assignable<V>::value, "");
604 test_constexpr_assign_imp<0>(v: V(42l), new_value: 101l);
605 test_constexpr_assign_imp<0>(v: V(nullptr), new_value: 101l);
606 test_constexpr_assign_imp<1>(v: V(42l), new_value: nullptr);
607 test_constexpr_assign_imp<2>(v: V(42l), new_value: 101);
608}
609
610struct NonTrivialCopyAssign {
611 int i = 0;
612 constexpr NonTrivialCopyAssign(int ii) : i(ii) {}
613 constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {}
614 constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) {
615 i = o.i;
616 return *this;
617 }
618 TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default;
619 friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; }
620};
621
622constexpr void test_constexpr_copy_assignment_non_trivial() {
623 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
624 using V = std::variant<long, void*, NonTrivialCopyAssign>;
625 static_assert(!std::is_trivially_copyable<V>::value, "");
626 static_assert(!std::is_trivially_copy_assignable<V>::value, "");
627 test_constexpr_assign_imp<0>(v: V(42l), new_value: 101l);
628 test_constexpr_assign_imp<0>(v: V(nullptr), new_value: 101l);
629 test_constexpr_assign_imp<1>(v: V(42l), new_value: nullptr);
630 test_constexpr_assign_imp<2>(v: V(42l), new_value: NonTrivialCopyAssign(5));
631 test_constexpr_assign_imp<2>(v: V(NonTrivialCopyAssign(3)), new_value: NonTrivialCopyAssign(5));
632}
633
634void non_constexpr_test() {
635 test_copy_assignment_empty_empty();
636 test_copy_assignment_non_empty_empty();
637 test_copy_assignment_empty_non_empty();
638 test_assignment_throw();
639}
640
641constexpr bool cxx17_constexpr_test() {
642 test_copy_assignment_sfinae();
643 test_copy_assignment_not_noexcept();
644 test_constexpr_copy_assignment_trivial();
645
646 return true;
647}
648
649TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
650 test_copy_assignment_same_index();
651 test_copy_assignment_different_index();
652 test_constexpr_copy_assignment_non_trivial();
653
654 return true;
655}
656
657int main(int, char**) {
658 non_constexpr_test();
659 cxx17_constexpr_test();
660 cxx20_constexpr_test();
661
662 static_assert(cxx17_constexpr_test());
663#if TEST_STD_VER >= 20
664 static_assert(cxx20_constexpr_test());
665#endif
666 return 0;
667}
668

source code of libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp