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