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