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 // void swap(variant& rhs) noexcept(see below)
19 
20 #include <cassert>
21 #include <string>
22 #include <type_traits>
23 #include <variant>
24 
25 #include "test_convertible.h"
26 #include "test_macros.h"
27 #include "variant_test_helpers.h"
28 
29 struct NotSwappable {};
30 void swap(NotSwappable &, NotSwappable &) = delete;
31 
32 struct NotCopyable {
33   NotCopyable() = default;
34   NotCopyable(const NotCopyable &) = delete;
35   NotCopyable &operator=(const NotCopyable &) = delete;
36 };
37 
38 struct NotCopyableWithSwap {
39   NotCopyableWithSwap() = default;
40   NotCopyableWithSwap(const NotCopyableWithSwap &) = delete;
41   NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete;
42 };
swap(NotCopyableWithSwap &,NotCopyableWithSwap)43 void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {}
44 
45 struct NotMoveAssignable {
46   NotMoveAssignable() = default;
47   NotMoveAssignable(NotMoveAssignable &&) = default;
48   NotMoveAssignable &operator=(NotMoveAssignable &&) = delete;
49 };
50 
51 struct NotMoveAssignableWithSwap {
52   NotMoveAssignableWithSwap() = default;
53   NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default;
54   NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete;
55 };
swap(NotMoveAssignableWithSwap &,NotMoveAssignableWithSwap &)56 void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {}
57 
do_throw()58 template <bool Throws> void do_throw() {}
59 
do_throw()60 template <> void do_throw<true>() {
61 #ifndef TEST_HAS_NO_EXCEPTIONS
62   throw 42;
63 #else
64   std::abort();
65 #endif
66 }
67 
68 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
69           bool NT_Swap, bool EnableSwap = true>
70 struct NothrowTypeImp {
71   static int move_called;
72   static int move_assign_called;
73   static int swap_called;
resetNothrowTypeImp74   static void reset() { move_called = move_assign_called = swap_called = 0; }
75   NothrowTypeImp() = default;
NothrowTypeImpNothrowTypeImp76   explicit NothrowTypeImp(int v) : value(v) {}
NothrowTypeImpNothrowTypeImp77   NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) {
78     assert(false);
79   } // never called by test
NothrowTypeImpNothrowTypeImp80   NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) {
81     ++move_called;
82     do_throw<!NT_Move>();
83     o.value = -1;
84   }
operator =NothrowTypeImp85   NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) {
86     assert(false);
87     return *this;
88   } // never called by the tests
operator =NothrowTypeImp89   NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) {
90     ++move_assign_called;
91     do_throw<!NT_MoveAssign>();
92     value = o.value;
93     o.value = -1;
94     return *this;
95   }
96   int value;
97 };
98 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
99           bool NT_Swap, bool EnableSwap>
100 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
101                    EnableSwap>::move_called = 0;
102 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
103           bool NT_Swap, bool EnableSwap>
104 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
105                    EnableSwap>::move_assign_called = 0;
106 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
107           bool NT_Swap, bool EnableSwap>
108 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
109                    EnableSwap>::swap_called = 0;
110 
111 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
112           bool NT_Swap>
swap(NothrowTypeImp<NT_Copy,NT_Move,NT_CopyAssign,NT_MoveAssign,NT_Swap,true> & lhs,NothrowTypeImp<NT_Copy,NT_Move,NT_CopyAssign,NT_MoveAssign,NT_Swap,true> & rhs)113 void swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
114                          NT_Swap, true> &lhs,
115           NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
116                          NT_Swap, true> &rhs) noexcept(NT_Swap) {
117   lhs.swap_called++;
118   do_throw<!NT_Swap>();
119   int tmp = lhs.value;
120   lhs.value = rhs.value;
121   rhs.value = tmp;
122 }
123 
124 // throwing copy, nothrow move ctor/assign, no swap provided
125 using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
126 // throwing copy and move assign, nothrow move ctor, no swap provided
127 using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
128 // nothrow move ctor, throwing move assignment, swap provided
129 using NothrowMoveCtorWithThrowingSwap =
130     NothrowTypeImp<false, true, false, false, false, true>;
131 // throwing move ctor, nothrow move assignment, no swap provided
132 using ThrowingMoveCtor =
133     NothrowTypeImp<false, false, false, true, false, false>;
134 // throwing special members, nothrowing swap
135 using ThrowingTypeWithNothrowSwap =
136     NothrowTypeImp<false, false, false, false, true, true>;
137 using NothrowTypeWithThrowingSwap =
138     NothrowTypeImp<true, true, true, true, false, true>;
139 // throwing move assign with nothrow move and nothrow swap
140 using ThrowingMoveAssignNothrowMoveCtorWithSwap =
141     NothrowTypeImp<false, true, false, false, true, true>;
142 // throwing move assign with nothrow move but no swap.
143 using ThrowingMoveAssignNothrowMoveCtor =
144     NothrowTypeImp<false, true, false, false, false, false>;
145 
146 struct NonThrowingNonNoexceptType {
147   static int move_called;
resetNonThrowingNonNoexceptType148   static void reset() { move_called = 0; }
149   NonThrowingNonNoexceptType() = default;
NonThrowingNonNoexceptTypeNonThrowingNonNoexceptType150   NonThrowingNonNoexceptType(int v) : value(v) {}
NonThrowingNonNoexceptTypeNonThrowingNonNoexceptType151   NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false)
152       : value(o.value) {
153     ++move_called;
154     o.value = -1;
155   }
156   NonThrowingNonNoexceptType &
operator =NonThrowingNonNoexceptType157   operator=(NonThrowingNonNoexceptType &&) noexcept(false) {
158     assert(false); // never called by the tests.
159     return *this;
160   }
161   int value;
162 };
163 int NonThrowingNonNoexceptType::move_called = 0;
164 
165 struct ThrowsOnSecondMove {
166   int value;
167   int move_count;
ThrowsOnSecondMoveThrowsOnSecondMove168   ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
ThrowsOnSecondMoveThrowsOnSecondMove169   ThrowsOnSecondMove(ThrowsOnSecondMove &&o) noexcept(false)
170       : value(o.value), move_count(o.move_count + 1) {
171     if (move_count == 2)
172       do_throw<true>();
173     o.value = -1;
174   }
operator =ThrowsOnSecondMove175   ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) {
176     assert(false); // not called by test
177     return *this;
178   }
179 };
180 
test_swap_valueless_by_exception()181 void test_swap_valueless_by_exception() {
182 #ifndef TEST_HAS_NO_EXCEPTIONS
183   using V = std::variant<int, MakeEmptyT>;
184   { // both empty
185     V v1;
186     makeEmpty(v1);
187     V v2;
188     makeEmpty(v2);
189     assert(MakeEmptyT::alive == 0);
190     { // member swap
191       v1.swap(v2);
192       assert(v1.valueless_by_exception());
193       assert(v2.valueless_by_exception());
194       assert(MakeEmptyT::alive == 0);
195     }
196     { // non-member swap
197       swap(v1, v2);
198       assert(v1.valueless_by_exception());
199       assert(v2.valueless_by_exception());
200       assert(MakeEmptyT::alive == 0);
201     }
202   }
203   { // only one empty
204     V v1(42);
205     V v2;
206     makeEmpty(v2);
207     { // member swap
208       v1.swap(v2);
209       assert(v1.valueless_by_exception());
210       assert(std::get<0>(v2) == 42);
211       // swap again
212       v2.swap(v1);
213       assert(v2.valueless_by_exception());
214       assert(std::get<0>(v1) == 42);
215     }
216     { // non-member swap
217       swap(v1, v2);
218       assert(v1.valueless_by_exception());
219       assert(std::get<0>(v2) == 42);
220       // swap again
221       swap(v1, v2);
222       assert(v2.valueless_by_exception());
223       assert(std::get<0>(v1) == 42);
224     }
225   }
226 #endif
227 }
228 
test_swap_same_alternative()229 void test_swap_same_alternative() {
230   {
231     using T = ThrowingTypeWithNothrowSwap;
232     using V = std::variant<T, int>;
233     T::reset();
234     V v1(std::in_place_index<0>, 42);
235     V v2(std::in_place_index<0>, 100);
236     v1.swap(v2);
237     assert(T::swap_called == 1);
238     assert(std::get<0>(v1).value == 100);
239     assert(std::get<0>(v2).value == 42);
240     swap(v1, v2);
241     assert(T::swap_called == 2);
242     assert(std::get<0>(v1).value == 42);
243     assert(std::get<0>(v2).value == 100);
244   }
245   {
246     using T = NothrowMoveable;
247     using V = std::variant<T, int>;
248     T::reset();
249     V v1(std::in_place_index<0>, 42);
250     V v2(std::in_place_index<0>, 100);
251     v1.swap(v2);
252     assert(T::swap_called == 0);
253     assert(T::move_called == 1);
254     assert(T::move_assign_called == 2);
255     assert(std::get<0>(v1).value == 100);
256     assert(std::get<0>(v2).value == 42);
257     T::reset();
258     swap(v1, v2);
259     assert(T::swap_called == 0);
260     assert(T::move_called == 1);
261     assert(T::move_assign_called == 2);
262     assert(std::get<0>(v1).value == 42);
263     assert(std::get<0>(v2).value == 100);
264   }
265 #ifndef TEST_HAS_NO_EXCEPTIONS
266   {
267     using T = NothrowTypeWithThrowingSwap;
268     using V = std::variant<T, int>;
269     T::reset();
270     V v1(std::in_place_index<0>, 42);
271     V v2(std::in_place_index<0>, 100);
272     try {
273       v1.swap(v2);
274       assert(false);
275     } catch (int) {
276     }
277     assert(T::swap_called == 1);
278     assert(T::move_called == 0);
279     assert(T::move_assign_called == 0);
280     assert(std::get<0>(v1).value == 42);
281     assert(std::get<0>(v2).value == 100);
282   }
283   {
284     using T = ThrowingMoveCtor;
285     using V = std::variant<T, int>;
286     T::reset();
287     V v1(std::in_place_index<0>, 42);
288     V v2(std::in_place_index<0>, 100);
289     try {
290       v1.swap(v2);
291       assert(false);
292     } catch (int) {
293     }
294     assert(T::move_called == 1); // call threw
295     assert(T::move_assign_called == 0);
296     assert(std::get<0>(v1).value ==
297            42); // throw happened before v1 was moved from
298     assert(std::get<0>(v2).value == 100);
299   }
300   {
301     using T = ThrowingMoveAssignNothrowMoveCtor;
302     using V = std::variant<T, int>;
303     T::reset();
304     V v1(std::in_place_index<0>, 42);
305     V v2(std::in_place_index<0>, 100);
306     try {
307       v1.swap(v2);
308       assert(false);
309     } catch (int) {
310     }
311     assert(T::move_called == 1);
312     assert(T::move_assign_called == 1);  // call threw and didn't complete
313     assert(std::get<0>(v1).value == -1); // v1 was moved from
314     assert(std::get<0>(v2).value == 100);
315   }
316 #endif
317 }
318 
test_swap_different_alternatives()319 void test_swap_different_alternatives() {
320   {
321     using T = NothrowMoveCtorWithThrowingSwap;
322     using V = std::variant<T, int>;
323     T::reset();
324     V v1(std::in_place_index<0>, 42);
325     V v2(std::in_place_index<1>, 100);
326     v1.swap(v2);
327     assert(T::swap_called == 0);
328     // The libc++ implementation double copies the argument, and not
329     // the variant swap is called on.
330     LIBCPP_ASSERT(T::move_called == 1);
331     assert(T::move_called <= 2);
332     assert(T::move_assign_called == 0);
333     assert(std::get<1>(v1) == 100);
334     assert(std::get<0>(v2).value == 42);
335     T::reset();
336     swap(v1, v2);
337     assert(T::swap_called == 0);
338     LIBCPP_ASSERT(T::move_called == 2);
339     assert(T::move_called <= 2);
340     assert(T::move_assign_called == 0);
341     assert(std::get<0>(v1).value == 42);
342     assert(std::get<1>(v2) == 100);
343   }
344 #ifndef TEST_HAS_NO_EXCEPTIONS
345   {
346     using T1 = ThrowingTypeWithNothrowSwap;
347     using T2 = NonThrowingNonNoexceptType;
348     using V = std::variant<T1, T2>;
349     T1::reset();
350     T2::reset();
351     V v1(std::in_place_index<0>, 42);
352     V v2(std::in_place_index<1>, 100);
353     try {
354       v1.swap(v2);
355       assert(false);
356     } catch (int) {
357     }
358     assert(T1::swap_called == 0);
359     assert(T1::move_called == 1); // throws
360     assert(T1::move_assign_called == 0);
361     // FIXME: libc++ shouldn't move from T2 here.
362     LIBCPP_ASSERT(T2::move_called == 1);
363     assert(T2::move_called <= 1);
364     assert(std::get<0>(v1).value == 42);
365     if (T2::move_called != 0)
366       assert(v2.valueless_by_exception());
367     else
368       assert(std::get<1>(v2).value == 100);
369   }
370   {
371     using T1 = NonThrowingNonNoexceptType;
372     using T2 = ThrowingTypeWithNothrowSwap;
373     using V = std::variant<T1, T2>;
374     T1::reset();
375     T2::reset();
376     V v1(std::in_place_index<0>, 42);
377     V v2(std::in_place_index<1>, 100);
378     try {
379       v1.swap(v2);
380       assert(false);
381     } catch (int) {
382     }
383     LIBCPP_ASSERT(T1::move_called == 0);
384     assert(T1::move_called <= 1);
385     assert(T2::swap_called == 0);
386     assert(T2::move_called == 1); // throws
387     assert(T2::move_assign_called == 0);
388     if (T1::move_called != 0)
389       assert(v1.valueless_by_exception());
390     else
391       assert(std::get<0>(v1).value == 42);
392     assert(std::get<1>(v2).value == 100);
393   }
394 // FIXME: The tests below are just very libc++ specific
395 #ifdef _LIBCPP_VERSION
396   {
397     using T1 = ThrowsOnSecondMove;
398     using T2 = NonThrowingNonNoexceptType;
399     using V = std::variant<T1, T2>;
400     T2::reset();
401     V v1(std::in_place_index<0>, 42);
402     V v2(std::in_place_index<1>, 100);
403     v1.swap(v2);
404     assert(T2::move_called == 2);
405     assert(std::get<1>(v1).value == 100);
406     assert(std::get<0>(v2).value == 42);
407     assert(std::get<0>(v2).move_count == 1);
408   }
409   {
410     using T1 = NonThrowingNonNoexceptType;
411     using T2 = ThrowsOnSecondMove;
412     using V = std::variant<T1, T2>;
413     T1::reset();
414     V v1(std::in_place_index<0>, 42);
415     V v2(std::in_place_index<1>, 100);
416     try {
417       v1.swap(v2);
418       assert(false);
419     } catch (int) {
420     }
421     assert(T1::move_called == 1);
422     assert(v1.valueless_by_exception());
423     assert(std::get<0>(v2).value == 42);
424   }
425 #endif
426 // testing libc++ extension. If either variant stores a nothrow move
427 // constructible type v1.swap(v2) provides the strong exception safety
428 // guarantee.
429 #ifdef _LIBCPP_VERSION
430   {
431 
432     using T1 = ThrowingTypeWithNothrowSwap;
433     using T2 = NothrowMoveable;
434     using V = std::variant<T1, T2>;
435     T1::reset();
436     T2::reset();
437     V v1(std::in_place_index<0>, 42);
438     V v2(std::in_place_index<1>, 100);
439     try {
440       v1.swap(v2);
441       assert(false);
442     } catch (int) {
443     }
444     assert(T1::swap_called == 0);
445     assert(T1::move_called == 1);
446     assert(T1::move_assign_called == 0);
447     assert(T2::swap_called == 0);
448     assert(T2::move_called == 2);
449     assert(T2::move_assign_called == 0);
450     assert(std::get<0>(v1).value == 42);
451     assert(std::get<1>(v2).value == 100);
452     // swap again, but call v2's swap.
453     T1::reset();
454     T2::reset();
455     try {
456       v2.swap(v1);
457       assert(false);
458     } catch (int) {
459     }
460     assert(T1::swap_called == 0);
461     assert(T1::move_called == 1);
462     assert(T1::move_assign_called == 0);
463     assert(T2::swap_called == 0);
464     assert(T2::move_called == 2);
465     assert(T2::move_assign_called == 0);
466     assert(std::get<0>(v1).value == 42);
467     assert(std::get<1>(v2).value == 100);
468   }
469 #endif // _LIBCPP_VERSION
470 #endif
471 }
472 
473 template <class Var>
has_swap_member_imp(int)474 constexpr auto has_swap_member_imp(int)
475     -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) {
476   return true;
477 }
478 
has_swap_member_imp(long)479 template <class Var> constexpr auto has_swap_member_imp(long) -> bool {
480   return false;
481 }
482 
has_swap_member()483 template <class Var> constexpr bool has_swap_member() {
484   return has_swap_member_imp<Var>(0);
485 }
486 
test_swap_sfinae()487 void test_swap_sfinae() {
488   {
489     // This variant type does not provide either a member or non-member swap
490     // but is still swappable via the generic swap algorithm, since the
491     // variant is move constructible and move assignable.
492     using V = std::variant<int, NotSwappable>;
493     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
494     static_assert(std::is_swappable_v<V>, "");
495   }
496   {
497     using V = std::variant<int, NotCopyable>;
498     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
499     static_assert(!std::is_swappable_v<V>, "");
500   }
501   {
502     using V = std::variant<int, NotCopyableWithSwap>;
503     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
504     static_assert(!std::is_swappable_v<V>, "");
505   }
506   {
507     using V = std::variant<int, NotMoveAssignable>;
508     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
509     static_assert(!std::is_swappable_v<V>, "");
510   }
511 }
512 
test_swap_noexcept()513 void test_swap_noexcept() {
514   {
515     using V = std::variant<int, NothrowMoveable>;
516     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
517     static_assert(std::is_nothrow_swappable_v<V>, "");
518     // instantiate swap
519     V v1, v2;
520     v1.swap(v2);
521     swap(v1, v2);
522   }
523   {
524     using V = std::variant<int, NothrowMoveCtor>;
525     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
526     static_assert(!std::is_nothrow_swappable_v<V>, "");
527     // instantiate swap
528     V v1, v2;
529     v1.swap(v2);
530     swap(v1, v2);
531   }
532   {
533     using V = std::variant<int, ThrowingTypeWithNothrowSwap>;
534     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
535     static_assert(!std::is_nothrow_swappable_v<V>, "");
536     // instantiate swap
537     V v1, v2;
538     v1.swap(v2);
539     swap(v1, v2);
540   }
541   {
542     using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtor>;
543     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
544     static_assert(!std::is_nothrow_swappable_v<V>, "");
545     // instantiate swap
546     V v1, v2;
547     v1.swap(v2);
548     swap(v1, v2);
549   }
550   {
551     using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtorWithSwap>;
552     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
553     static_assert(std::is_nothrow_swappable_v<V>, "");
554     // instantiate swap
555     V v1, v2;
556     v1.swap(v2);
557     swap(v1, v2);
558   }
559   {
560     using V = std::variant<int, NotMoveAssignableWithSwap>;
561     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
562     static_assert(std::is_nothrow_swappable_v<V>, "");
563     // instantiate swap
564     V v1, v2;
565     v1.swap(v2);
566     swap(v1, v2);
567   }
568   {
569     // This variant type does not provide either a member or non-member swap
570     // but is still swappable via the generic swap algorithm, since the
571     // variant is move constructible and move assignable.
572     using V = std::variant<int, NotSwappable>;
573     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
574     static_assert(std::is_swappable_v<V>, "");
575     static_assert(std::is_nothrow_swappable_v<V>, "");
576     V v1, v2;
577     swap(v1, v2);
578   }
579 }
580 
581 #ifdef _LIBCPP_VERSION
582 // This is why variant should SFINAE member swap. :-)
583 template class std::variant<int, NotSwappable>;
584 #endif
585 
main(int,char **)586 int main(int, char**) {
587   test_swap_valueless_by_exception();
588   test_swap_same_alternative();
589   test_swap_different_alternatives();
590   test_swap_sfinae();
591   test_swap_noexcept();
592 
593   return 0;
594 }
595