• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&); // constexpr in C++20
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;
MoveOnlyNTMoveOnlyNT58   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;
resetCopyAssign68   static void reset() {
69     copy_construct = copy_assign = move_construct = move_assign = alive = 0;
70   }
CopyAssignCopyAssign71   CopyAssign(int v) : value(v) { ++alive; }
CopyAssignCopyAssign72   CopyAssign(const CopyAssign &o) : value(o.value) {
73     ++alive;
74     ++copy_construct;
75   }
CopyAssignCopyAssign76   CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
77     o.value = -1;
78     ++alive;
79     ++move_construct;
80   }
operator =CopyAssign81   CopyAssign &operator=(const CopyAssign &o) {
82     value = o.value;
83     ++copy_assign;
84     return *this;
85   }
operator =CopyAssign86   CopyAssign &operator=(CopyAssign &&o) noexcept {
87     value = o.value;
88     o.value = -1;
89     ++move_assign;
90     return *this;
91   }
~CopyAssignCopyAssign92   ~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 {
NTCopyAssignNTCopyAssign113   constexpr NTCopyAssign(int v) : value(v) {}
114   NTCopyAssign(const NTCopyAssign &) = default;
115   NTCopyAssign(NTCopyAssign &&) = default;
operator =NTCopyAssign116   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 {
TCopyAssignTCopyAssign128   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 {
TCopyAssignNTMoveAssignTCopyAssignNTMoveAssign139   constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
140   TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default;
141   TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default;
142   TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default;
operator =TCopyAssignNTMoveAssign143   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;
CopyThrowsCopyThrows156   CopyThrows(const CopyThrows &) { throw 42; }
operator =CopyThrows157   CopyThrows &operator=(const CopyThrows &) { throw 42; }
158 };
159 
160 struct CopyCannotThrow {
161   static int alive;
CopyCannotThrowCopyCannotThrow162   CopyCannotThrow() { ++alive; }
CopyCannotThrowCopyCannotThrow163   CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
CopyCannotThrowCopyCannotThrow164   CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
165   CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
operator =CopyCannotThrow166   CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
167 };
168 
169 int CopyCannotThrow::alive = 0;
170 
171 struct MoveThrows {
172   static int alive;
MoveThrowsMoveThrows173   MoveThrows() { ++alive; }
MoveThrowsMoveThrows174   MoveThrows(const MoveThrows &) { ++alive; }
MoveThrowsMoveThrows175   MoveThrows(MoveThrows &&) { throw 42; }
operator =MoveThrows176   MoveThrows &operator=(const MoveThrows &) { return *this; }
operator =MoveThrows177   MoveThrows &operator=(MoveThrows &&) { throw 42; }
~MoveThrowsMoveThrows178   ~MoveThrows() { --alive; }
179 };
180 
181 int MoveThrows::alive = 0;
182 
183 struct MakeEmptyT {
184   static int alive;
MakeEmptyTMakeEmptyT185   MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT186   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   }
MakeEmptyTMakeEmptyT191   MakeEmptyT(MakeEmptyT &&) { throw 42; }
operator =MakeEmptyT192   MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
operator =MakeEmptyT193   MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
~MakeEmptyTMakeEmptyT194   ~MakeEmptyT() { --alive; }
195 };
196 
197 int MakeEmptyT::alive = 0;
198 
makeEmpty(Variant & v)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 
test_copy_assignment_not_noexcept()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 
test_copy_assignment_sfinae()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   // Make sure we properly propagate triviality (see P0602R4).
244 #if TEST_STD_VER > 17
245   {
246     using V = std::variant<int, long>;
247     static_assert(std::is_trivially_copy_assignable<V>::value, "");
248   }
249   {
250     using V = std::variant<int, NTCopyAssign>;
251     static_assert(!std::is_trivially_copy_assignable<V>::value, "");
252     static_assert(std::is_copy_assignable<V>::value, "");
253   }
254   {
255     using V = std::variant<int, TCopyAssign>;
256     static_assert(std::is_trivially_copy_assignable<V>::value, "");
257   }
258   {
259     using V = std::variant<int, TCopyAssignNTMoveAssign>;
260     static_assert(std::is_trivially_copy_assignable<V>::value, "");
261   }
262   {
263     using V = std::variant<int, CopyOnly>;
264     static_assert(std::is_trivially_copy_assignable<V>::value, "");
265   }
266 #endif // > C++17
267 }
268 
test_copy_assignment_empty_empty()269 void test_copy_assignment_empty_empty() {
270 #ifndef TEST_HAS_NO_EXCEPTIONS
271   using MET = MakeEmptyT;
272   {
273     using V = std::variant<int, long, MET>;
274     V v1(std::in_place_index<0>);
275     makeEmpty(v1);
276     V v2(std::in_place_index<0>);
277     makeEmpty(v2);
278     V &vref = (v1 = v2);
279     assert(&vref == &v1);
280     assert(v1.valueless_by_exception());
281     assert(v1.index() == std::variant_npos);
282   }
283 #endif // TEST_HAS_NO_EXCEPTIONS
284 }
285 
test_copy_assignment_non_empty_empty()286 void test_copy_assignment_non_empty_empty() {
287 #ifndef TEST_HAS_NO_EXCEPTIONS
288   using MET = MakeEmptyT;
289   {
290     using V = std::variant<int, MET>;
291     V v1(std::in_place_index<0>, 42);
292     V v2(std::in_place_index<0>);
293     makeEmpty(v2);
294     V &vref = (v1 = v2);
295     assert(&vref == &v1);
296     assert(v1.valueless_by_exception());
297     assert(v1.index() == std::variant_npos);
298   }
299   {
300     using V = std::variant<int, MET, std::string>;
301     V v1(std::in_place_index<2>, "hello");
302     V v2(std::in_place_index<0>);
303     makeEmpty(v2);
304     V &vref = (v1 = v2);
305     assert(&vref == &v1);
306     assert(v1.valueless_by_exception());
307     assert(v1.index() == std::variant_npos);
308   }
309 #endif // TEST_HAS_NO_EXCEPTIONS
310 }
311 
test_copy_assignment_empty_non_empty()312 void test_copy_assignment_empty_non_empty() {
313 #ifndef TEST_HAS_NO_EXCEPTIONS
314   using MET = MakeEmptyT;
315   {
316     using V = std::variant<int, MET>;
317     V v1(std::in_place_index<0>);
318     makeEmpty(v1);
319     V v2(std::in_place_index<0>, 42);
320     V &vref = (v1 = v2);
321     assert(&vref == &v1);
322     assert(v1.index() == 0);
323     assert(std::get<0>(v1) == 42);
324   }
325   {
326     using V = std::variant<int, MET, std::string>;
327     V v1(std::in_place_index<0>);
328     makeEmpty(v1);
329     V v2(std::in_place_type<std::string>, "hello");
330     V &vref = (v1 = v2);
331     assert(&vref == &v1);
332     assert(v1.index() == 2);
333     assert(std::get<2>(v1) == "hello");
334   }
335 #endif // TEST_HAS_NO_EXCEPTIONS
336 }
337 
338 template <typename T> struct Result { size_t index; T value; };
339 
test_copy_assignment_same_index()340 void test_copy_assignment_same_index() {
341   {
342     using V = std::variant<int>;
343     V v1(43);
344     V v2(42);
345     V &vref = (v1 = v2);
346     assert(&vref == &v1);
347     assert(v1.index() == 0);
348     assert(std::get<0>(v1) == 42);
349   }
350   {
351     using V = std::variant<int, long, unsigned>;
352     V v1(43l);
353     V v2(42l);
354     V &vref = (v1 = v2);
355     assert(&vref == &v1);
356     assert(v1.index() == 1);
357     assert(std::get<1>(v1) == 42);
358   }
359   {
360     using V = std::variant<int, CopyAssign, unsigned>;
361     V v1(std::in_place_type<CopyAssign>, 43);
362     V v2(std::in_place_type<CopyAssign>, 42);
363     CopyAssign::reset();
364     V &vref = (v1 = v2);
365     assert(&vref == &v1);
366     assert(v1.index() == 1);
367     assert(std::get<1>(v1).value == 42);
368     assert(CopyAssign::copy_construct == 0);
369     assert(CopyAssign::move_construct == 0);
370     assert(CopyAssign::copy_assign == 1);
371   }
372 #ifndef TEST_HAS_NO_EXCEPTIONS
373   using MET = MakeEmptyT;
374   {
375     using V = std::variant<int, MET, std::string>;
376     V v1(std::in_place_type<MET>);
377     MET &mref = std::get<1>(v1);
378     V v2(std::in_place_type<MET>);
379     try {
380       v1 = v2;
381       assert(false);
382     } catch (...) {
383     }
384     assert(v1.index() == 1);
385     assert(&std::get<1>(v1) == &mref);
386   }
387 #endif // TEST_HAS_NO_EXCEPTIONS
388 
389   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
390 #if TEST_STD_VER > 17
391   {
392     struct {
393       constexpr Result<int> operator()() const {
394         using V = std::variant<int>;
395         V v(43);
396         V v2(42);
397         v = v2;
398         return {v.index(), std::get<0>(v)};
399       }
400     } test;
401     constexpr auto result = test();
402     static_assert(result.index == 0, "");
403     static_assert(result.value == 42, "");
404   }
405   {
406     struct {
407       constexpr Result<long> operator()() const {
408         using V = std::variant<int, long, unsigned>;
409         V v(43l);
410         V v2(42l);
411         v = v2;
412         return {v.index(), std::get<1>(v)};
413       }
414     } test;
415     constexpr auto result = test();
416     static_assert(result.index == 1, "");
417     static_assert(result.value == 42l, "");
418   }
419   {
420     struct {
421       constexpr Result<int> operator()() const {
422         using V = std::variant<int, TCopyAssign, unsigned>;
423         V v(std::in_place_type<TCopyAssign>, 43);
424         V v2(std::in_place_type<TCopyAssign>, 42);
425         v = v2;
426         return {v.index(), std::get<1>(v).value};
427       }
428     } test;
429     constexpr auto result = test();
430     static_assert(result.index == 1, "");
431     static_assert(result.value == 42, "");
432   }
433   {
434     struct {
435       constexpr Result<int> operator()() const {
436         using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;
437         V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);
438         V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);
439         v = v2;
440         return {v.index(), std::get<1>(v).value};
441       }
442     } test;
443     constexpr auto result = test();
444     static_assert(result.index == 1, "");
445     static_assert(result.value == 42, "");
446   }
447 #endif // > C++17
448 }
449 
test_copy_assignment_different_index()450 void test_copy_assignment_different_index() {
451   {
452     using V = std::variant<int, long, unsigned>;
453     V v1(43);
454     V v2(42l);
455     V &vref = (v1 = v2);
456     assert(&vref == &v1);
457     assert(v1.index() == 1);
458     assert(std::get<1>(v1) == 42);
459   }
460   {
461     using V = std::variant<int, CopyAssign, unsigned>;
462     CopyAssign::reset();
463     V v1(std::in_place_type<unsigned>, 43);
464     V v2(std::in_place_type<CopyAssign>, 42);
465     assert(CopyAssign::copy_construct == 0);
466     assert(CopyAssign::move_construct == 0);
467     assert(CopyAssign::alive == 1);
468     V &vref = (v1 = v2);
469     assert(&vref == &v1);
470     assert(v1.index() == 1);
471     assert(std::get<1>(v1).value == 42);
472     assert(CopyAssign::alive == 2);
473     assert(CopyAssign::copy_construct == 1);
474     assert(CopyAssign::move_construct == 1);
475     assert(CopyAssign::copy_assign == 0);
476   }
477 #ifndef TEST_HAS_NO_EXCEPTIONS
478   {
479     using V = std::variant<int, CopyThrows, std::string>;
480     V v1(std::in_place_type<std::string>, "hello");
481     V v2(std::in_place_type<CopyThrows>);
482     try {
483       v1 = v2;
484       assert(false);
485     } catch (...) { /* ... */
486     }
487     // Test that copy construction is used directly if move construction may throw,
488     // resulting in a valueless variant if copy throws.
489     assert(v1.valueless_by_exception());
490   }
491   {
492     using V = std::variant<int, MoveThrows, std::string>;
493     V v1(std::in_place_type<std::string>, "hello");
494     V v2(std::in_place_type<MoveThrows>);
495     assert(MoveThrows::alive == 1);
496     // Test that copy construction is used directly if move construction may throw.
497     v1 = v2;
498     assert(v1.index() == 1);
499     assert(v2.index() == 1);
500     assert(MoveThrows::alive == 2);
501   }
502   {
503     // Test that direct copy construction is preferred when it cannot throw.
504     using V = std::variant<int, CopyCannotThrow, std::string>;
505     V v1(std::in_place_type<std::string>, "hello");
506     V v2(std::in_place_type<CopyCannotThrow>);
507     assert(CopyCannotThrow::alive == 1);
508     v1 = v2;
509     assert(v1.index() == 1);
510     assert(v2.index() == 1);
511     assert(CopyCannotThrow::alive == 2);
512   }
513   {
514     using V = std::variant<int, CopyThrows, std::string>;
515     V v1(std::in_place_type<CopyThrows>);
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   {
525     using V = std::variant<int, MoveThrows, std::string>;
526     V v1(std::in_place_type<MoveThrows>);
527     V v2(std::in_place_type<std::string>, "hello");
528     V &vref = (v1 = v2);
529     assert(&vref == &v1);
530     assert(v1.index() == 2);
531     assert(std::get<2>(v1) == "hello");
532     assert(v2.index() == 2);
533     assert(std::get<2>(v2) == "hello");
534   }
535 #endif // TEST_HAS_NO_EXCEPTIONS
536 
537   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
538 #if TEST_STD_VER > 17
539   {
540     struct {
541       constexpr Result<long> operator()() const {
542         using V = std::variant<int, long, unsigned>;
543         V v(43);
544         V v2(42l);
545         v = v2;
546         return {v.index(), std::get<1>(v)};
547       }
548     } test;
549     constexpr auto result = test();
550     static_assert(result.index == 1, "");
551     static_assert(result.value == 42l, "");
552   }
553   {
554     struct {
555       constexpr Result<int> operator()() const {
556         using V = std::variant<int, TCopyAssign, unsigned>;
557         V v(std::in_place_type<unsigned>, 43);
558         V v2(std::in_place_type<TCopyAssign>, 42);
559         v = v2;
560         return {v.index(), std::get<1>(v).value};
561       }
562     } test;
563     constexpr auto result = test();
564     static_assert(result.index == 1, "");
565     static_assert(result.value == 42, "");
566   }
567 #endif // > C++17
568 }
569 
570 template <size_t NewIdx, class ValueType>
test_constexpr_assign_imp(std::variant<long,void *,int> && v,ValueType && new_value)571 constexpr bool test_constexpr_assign_imp(
572     std::variant<long, void*, int>&& v, ValueType&& new_value)
573 {
574   const std::variant<long, void*, int> cp(
575       std::forward<ValueType>(new_value));
576   v = cp;
577   return v.index() == NewIdx &&
578         std::get<NewIdx>(v) == std::get<NewIdx>(cp);
579 }
580 
test_constexpr_copy_assignment()581 void test_constexpr_copy_assignment() {
582   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
583 #if TEST_STD_VER > 17
584   using V = std::variant<long, void*, int>;
585   static_assert(std::is_trivially_copyable<V>::value, "");
586   static_assert(std::is_trivially_copy_assignable<V>::value, "");
587   static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
588   static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
589   static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
590   static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
591 #endif // > C++17
592 }
593 
main()594 int main() {
595   test_copy_assignment_empty_empty();
596   test_copy_assignment_non_empty_empty();
597   test_copy_assignment_empty_non_empty();
598   test_copy_assignment_same_index();
599   test_copy_assignment_different_index();
600   test_copy_assignment_sfinae();
601   test_copy_assignment_not_noexcept();
602   test_constexpr_copy_assignment();
603 }
604