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