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