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