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