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