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