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