xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp (revision 2ba08386156ef25913b1bee170d8fe95aaceb234)
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 constexpr 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 constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {}
55 
56 template <bool Throws>
do_throw()57 constexpr void do_throw() {}
58 
59 template <>
do_throw()60 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, bool NT_Swap, bool EnableSwap = true>
69 struct NothrowTypeImp {
70   int value;
71   int* move_called;
72   int* move_assign_called;
73   int* swap_called;
74 
NothrowTypeImpNothrowTypeImp75   constexpr NothrowTypeImp(int v, int* mv_ctr, int* mv_assign, int* swap)
76       : value(v), move_called(mv_ctr), move_assign_called(mv_assign), swap_called(swap) {}
77 
NothrowTypeImpNothrowTypeImp78   NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test
79 
NothrowTypeImpNothrowTypeImp80   constexpr NothrowTypeImp(NothrowTypeImp&& o) noexcept(NT_Move)
81       : value(o.value),
82         move_called(o.move_called),
83         move_assign_called(o.move_assign_called),
84         swap_called(o.swap_called) {
85     ++*move_called;
86     do_throw<!NT_Move>();
87     o.value = -1;
88   }
89 
operator =NothrowTypeImp90   NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) {
91     assert(false);
92     return *this;
93   } // never called by the tests
94 
operator =NothrowTypeImp95   constexpr NothrowTypeImp& operator=(NothrowTypeImp&& o) noexcept(NT_MoveAssign) {
96     ++*move_assign_called;
97     do_throw<!NT_MoveAssign>();
98     value   = o.value;
99     o.value = -1;
100     return *this;
101   }
102 };
103 
104 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap>
105 constexpr void
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)106 swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& lhs,
107      NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& rhs) noexcept(NT_Swap) {
108   ++*lhs.swap_called;
109   do_throw<!NT_Swap>();
110   std::swap(lhs.value, rhs.value);
111 }
112 
113 // throwing copy, nothrow move ctor/assign, no swap provided
114 using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
115 // throwing copy and move assign, nothrow move ctor, no swap provided
116 using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
117 // nothrow move ctor, throwing move assignment, swap provided
118 using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp<false, true, false, false, false, true>;
119 // throwing move ctor, nothrow move assignment, no swap provided
120 using ThrowingMoveCtor = NothrowTypeImp<false, false, false, true, false, false>;
121 // throwing special members, nothrowing swap
122 using ThrowingTypeWithNothrowSwap = NothrowTypeImp<false, false, false, false, true, true>;
123 using NothrowTypeWithThrowingSwap = NothrowTypeImp<true, true, true, true, false, true>;
124 // throwing move assign with nothrow move and nothrow swap
125 using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp<false, true, false, false, true, true>;
126 // throwing move assign with nothrow move but no swap.
127 using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
128 
129 struct NonThrowingNonNoexceptType {
130   int value;
131   int* move_called;
NonThrowingNonNoexceptTypeNonThrowingNonNoexceptType132   constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {}
NonThrowingNonNoexceptTypeNonThrowingNonNoexceptType133   constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false)
134       : value(o.value), move_called(o.move_called) {
135     ++*move_called;
136     o.value = -1;
137   }
operator =NonThrowingNonNoexceptType138   NonThrowingNonNoexceptType& operator=(NonThrowingNonNoexceptType&&) noexcept(false) {
139     assert(false); // never called by the tests.
140     return *this;
141   }
142 };
143 
144 struct ThrowsOnSecondMove {
145   int value;
146   int move_count;
ThrowsOnSecondMoveThrowsOnSecondMove147   ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
ThrowsOnSecondMoveThrowsOnSecondMove148   ThrowsOnSecondMove(ThrowsOnSecondMove&& o) noexcept(false) : value(o.value), move_count(o.move_count + 1) {
149     if (move_count == 2)
150       do_throw<true>();
151     o.value = -1;
152   }
operator =ThrowsOnSecondMove153   ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) {
154     assert(false); // not called by test
155     return *this;
156   }
157 };
158 
test_swap_valueless_by_exception()159 void test_swap_valueless_by_exception() {
160 #ifndef TEST_HAS_NO_EXCEPTIONS
161   using V = std::variant<int, MakeEmptyT>;
162   { // both empty
163     V v1;
164     makeEmpty(v1);
165     V v2;
166     makeEmpty(v2);
167     assert(MakeEmptyT::alive == 0);
168     { // member swap
169       v1.swap(v2);
170       assert(v1.valueless_by_exception());
171       assert(v2.valueless_by_exception());
172       assert(MakeEmptyT::alive == 0);
173     }
174     { // non-member swap
175       swap(v1, v2);
176       assert(v1.valueless_by_exception());
177       assert(v2.valueless_by_exception());
178       assert(MakeEmptyT::alive == 0);
179     }
180   }
181   { // only one empty
182     V v1(42);
183     V v2;
184     makeEmpty(v2);
185     { // member swap
186       v1.swap(v2);
187       assert(v1.valueless_by_exception());
188       assert(std::get<0>(v2) == 42);
189       // swap again
190       v2.swap(v1);
191       assert(v2.valueless_by_exception());
192       assert(std::get<0>(v1) == 42);
193     }
194     { // non-member swap
195       swap(v1, v2);
196       assert(v1.valueless_by_exception());
197       assert(std::get<0>(v2) == 42);
198       // swap again
199       swap(v1, v2);
200       assert(v2.valueless_by_exception());
201       assert(std::get<0>(v1) == 42);
202     }
203   }
204 #endif
205 }
206 
test_swap_same_alternative()207 TEST_CONSTEXPR_CXX20 void test_swap_same_alternative() {
208   {
209     using V                = std::variant<ThrowingTypeWithNothrowSwap, int>;
210     int move_called        = 0;
211     int move_assign_called = 0;
212     int swap_called        = 0;
213     V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
214     V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
215     v1.swap(v2);
216     assert(swap_called == 1);
217     assert(std::get<0>(v1).value == 100);
218     assert(std::get<0>(v2).value == 42);
219     swap(v1, v2);
220     assert(swap_called == 2);
221     assert(std::get<0>(v1).value == 42);
222     assert(std::get<0>(v2).value == 100);
223 
224     assert(move_called == 0);
225     assert(move_assign_called == 0);
226   }
227   {
228     using V                = std::variant<NothrowMoveable, int>;
229     int move_called        = 0;
230     int move_assign_called = 0;
231     int swap_called        = 0;
232     V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
233     V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
234     v1.swap(v2);
235     assert(swap_called == 0);
236     assert(move_called == 1);
237     assert(move_assign_called == 2);
238     assert(std::get<0>(v1).value == 100);
239     assert(std::get<0>(v2).value == 42);
240 
241     move_called        = 0;
242     move_assign_called = 0;
243     swap_called        = 0;
244 
245     swap(v1, v2);
246     assert(swap_called == 0);
247     assert(move_called == 1);
248     assert(move_assign_called == 2);
249     assert(std::get<0>(v1).value == 42);
250     assert(std::get<0>(v2).value == 100);
251   }
252 }
253 
test_swap_same_alternative_throws()254 void test_swap_same_alternative_throws(){
255 #ifndef TEST_HAS_NO_EXCEPTIONS
256     {using V = std::variant<NothrowTypeWithThrowingSwap, int>;
257 int move_called        = 0;
258 int move_assign_called = 0;
259 int swap_called        = 0;
260 V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
261 V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
262 try {
263   v1.swap(v2);
264   assert(false);
265 } catch (int) {
266 }
267 assert(swap_called == 1);
268 assert(move_called == 0);
269 assert(move_assign_called == 0);
270 assert(std::get<0>(v1).value == 42);
271 assert(std::get<0>(v2).value == 100);
272 }
273 
274 {
275   using V                = std::variant<ThrowingMoveCtor, int>;
276   int move_called        = 0;
277   int move_assign_called = 0;
278   int swap_called        = 0;
279   V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
280   V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
281   try {
282     v1.swap(v2);
283     assert(false);
284   } catch (int) {
285   }
286   assert(move_called == 1); // call threw
287   assert(move_assign_called == 0);
288   assert(swap_called == 0);
289   assert(std::get<0>(v1).value == 42); // throw happened before v1 was moved from
290   assert(std::get<0>(v2).value == 100);
291 }
292 {
293   using V                = std::variant<ThrowingMoveAssignNothrowMoveCtor, int>;
294   int move_called        = 0;
295   int move_assign_called = 0;
296   int swap_called        = 0;
297   V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
298   V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
299   try {
300     v1.swap(v2);
301     assert(false);
302   } catch (int) {
303   }
304   assert(move_called == 1);
305   assert(move_assign_called == 1); // call threw and didn't complete
306   assert(swap_called == 0);
307   assert(std::get<0>(v1).value == -1); // v1 was moved from
308   assert(std::get<0>(v2).value == 100);
309 }
310 #endif
311 }
312 
test_swap_different_alternatives()313 TEST_CONSTEXPR_CXX20 void test_swap_different_alternatives() {
314   {
315     using V                = std::variant<NothrowMoveCtorWithThrowingSwap, int>;
316     int move_called        = 0;
317     int move_assign_called = 0;
318     int swap_called        = 0;
319     V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
320     V v2(std::in_place_index<1>, 100);
321     v1.swap(v2);
322     assert(swap_called == 0);
323     // The libc++ implementation double copies the argument, and not
324     // the variant swap is called on.
325     LIBCPP_ASSERT(move_called == 1);
326     assert(move_called <= 2);
327     assert(move_assign_called == 0);
328     assert(std::get<1>(v1) == 100);
329     assert(std::get<0>(v2).value == 42);
330 
331     move_called        = 0;
332     move_assign_called = 0;
333     swap_called        = 0;
334 
335     swap(v1, v2);
336     assert(swap_called == 0);
337     LIBCPP_ASSERT(move_called == 2);
338     assert(move_called <= 2);
339     assert(move_assign_called == 0);
340     assert(std::get<0>(v1).value == 42);
341     assert(std::get<1>(v2) == 100);
342   }
343 }
344 
test_swap_different_alternatives_throws()345 void test_swap_different_alternatives_throws() {
346 #ifndef TEST_HAS_NO_EXCEPTIONS
347   {
348     using V                 = std::variant<ThrowingTypeWithNothrowSwap, NonThrowingNonNoexceptType>;
349     int move_called1        = 0;
350     int move_assign_called1 = 0;
351     int swap_called1        = 0;
352     int move_called2        = 0;
353     V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);
354     V v2(std::in_place_index<1>, 100, &move_called2);
355     try {
356       v1.swap(v2);
357       assert(false);
358     } catch (int) {
359     }
360     assert(swap_called1 == 0);
361     assert(move_called1 == 1); // throws
362     assert(move_assign_called1 == 0);
363     // FIXME: libc++ shouldn't move from T2 here.
364     LIBCPP_ASSERT(move_called2 == 1);
365     assert(move_called2 <= 1);
366     assert(std::get<0>(v1).value == 42);
367     if (move_called2 != 0)
368       assert(v2.valueless_by_exception());
369     else
370       assert(std::get<1>(v2).value == 100);
371   }
372   {
373     using V                 = std::variant<NonThrowingNonNoexceptType, ThrowingTypeWithNothrowSwap>;
374     int move_called1        = 0;
375     int move_called2        = 0;
376     int move_assign_called2 = 0;
377     int swap_called2        = 0;
378     V v1(std::in_place_index<0>, 42, &move_called1);
379     V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);
380     try {
381       v1.swap(v2);
382       assert(false);
383     } catch (int) {
384     }
385     LIBCPP_ASSERT(move_called1 == 0);
386     assert(move_called1 <= 1);
387     assert(swap_called2 == 0);
388     assert(move_called2 == 1); // throws
389     assert(move_assign_called2 == 0);
390     if (move_called1 != 0)
391       assert(v1.valueless_by_exception());
392     else
393       assert(std::get<0>(v1).value == 42);
394     assert(std::get<1>(v2).value == 100);
395   }
396 // FIXME: The tests below are just very libc++ specific
397 #  ifdef _LIBCPP_VERSION
398   {
399     using V         = std::variant<ThrowsOnSecondMove, NonThrowingNonNoexceptType>;
400     int move_called = 0;
401     V v1(std::in_place_index<0>, 42);
402     V v2(std::in_place_index<1>, 100, &move_called);
403     v1.swap(v2);
404     assert(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 V         = std::variant<NonThrowingNonNoexceptType, ThrowsOnSecondMove>;
411     int move_called = 0;
412     V v1(std::in_place_index<0>, 42, &move_called);
413     V v2(std::in_place_index<1>, 100);
414     try {
415       v1.swap(v2);
416       assert(false);
417     } catch (int) {
418     }
419     assert(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     using V                 = std::variant<ThrowingTypeWithNothrowSwap, NothrowMoveable>;
430     int move_called1        = 0;
431     int move_assign_called1 = 0;
432     int swap_called1        = 0;
433     int move_called2        = 0;
434     int move_assign_called2 = 0;
435     int swap_called2        = 0;
436     V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);
437     V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);
438     try {
439       v1.swap(v2);
440       assert(false);
441     } catch (int) {
442     }
443     assert(swap_called1 == 0);
444     assert(move_called1 == 1);
445     assert(move_assign_called1 == 0);
446     assert(swap_called2 == 0);
447     assert(move_called2 == 2);
448     assert(move_assign_called2 == 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 
453     move_called1        = 0;
454     move_assign_called1 = 0;
455     swap_called1        = 0;
456     move_called2        = 0;
457     move_assign_called2 = 0;
458     swap_called2        = 0;
459 
460     try {
461       v2.swap(v1);
462       assert(false);
463     } catch (int) {
464     }
465     assert(swap_called1 == 0);
466     assert(move_called1 == 1);
467     assert(move_assign_called1 == 0);
468     assert(swap_called2 == 0);
469     assert(move_called2 == 2);
470     assert(move_assign_called2 == 0);
471     assert(std::get<0>(v1).value == 42);
472     assert(std::get<1>(v2).value == 100);
473   }
474 #  endif // _LIBCPP_VERSION
475 #endif
476 }
477 
478 template <class Var>
has_swap_member_imp(int)479 constexpr auto has_swap_member_imp(int) -> decltype(std::declval<Var&>().swap(std::declval<Var&>()), true) {
480   return true;
481 }
482 
483 template <class Var>
has_swap_member_imp(long)484 constexpr auto has_swap_member_imp(long) -> bool {
485   return false;
486 }
487 
488 template <class Var>
has_swap_member()489 constexpr bool has_swap_member() {
490   return has_swap_member_imp<Var>(0);
491 }
492 
test_swap_sfinae()493 constexpr void test_swap_sfinae() {
494   {
495     // This variant type does not provide either a member or non-member swap
496     // but is still swappable via the generic swap algorithm, since the
497     // variant is move constructible and move assignable.
498     using V = std::variant<int, NotSwappable>;
499     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
500     static_assert(std::is_swappable_v<V>, "");
501   }
502   {
503     using V = std::variant<int, NotCopyable>;
504     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
505     static_assert(!std::is_swappable_v<V>, "");
506   }
507   {
508     using V = std::variant<int, NotCopyableWithSwap>;
509     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
510     static_assert(!std::is_swappable_v<V>, "");
511   }
512   {
513     using V = std::variant<int, NotMoveAssignable>;
514     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
515     static_assert(!std::is_swappable_v<V>, "");
516   }
517 }
518 
test_swap_noexcept()519 TEST_CONSTEXPR_CXX20 void test_swap_noexcept() {
520   {
521     using V = std::variant<int, NothrowMoveable>;
522     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
523     static_assert(std::is_nothrow_swappable_v<V>, "");
524     // instantiate swap
525     V v1, v2;
526     v1.swap(v2);
527     swap(v1, v2);
528   }
529   {
530     using V = std::variant<int, NothrowMoveCtor>;
531     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
532     static_assert(!std::is_nothrow_swappable_v<V>, "");
533     // instantiate swap
534     V v1, v2;
535     v1.swap(v2);
536     swap(v1, v2);
537   }
538   {
539     using V = std::variant<int, ThrowingTypeWithNothrowSwap>;
540     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
541     static_assert(!std::is_nothrow_swappable_v<V>, "");
542     // instantiate swap
543     V v1, v2;
544     v1.swap(v2);
545     swap(v1, v2);
546   }
547   {
548     using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtor>;
549     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
550     static_assert(!std::is_nothrow_swappable_v<V>, "");
551     // instantiate swap
552     V v1, v2;
553     v1.swap(v2);
554     swap(v1, v2);
555   }
556   {
557     using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtorWithSwap>;
558     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
559     static_assert(std::is_nothrow_swappable_v<V>, "");
560     // instantiate swap
561     V v1, v2;
562     v1.swap(v2);
563     swap(v1, v2);
564   }
565   {
566     using V = std::variant<int, NotMoveAssignableWithSwap>;
567     static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
568     static_assert(std::is_nothrow_swappable_v<V>, "");
569     // instantiate swap
570     V v1, v2;
571     v1.swap(v2);
572     swap(v1, v2);
573   }
574   {
575     // This variant type does not provide either a member or non-member swap
576     // but is still swappable via the generic swap algorithm, since the
577     // variant is move constructible and move assignable.
578     using V = std::variant<int, NotSwappable>;
579     LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), "");
580     static_assert(std::is_swappable_v<V>, "");
581     static_assert(std::is_nothrow_swappable_v<V>, "");
582     V v1, v2;
583     swap(v1, v2);
584   }
585 }
586 
587 #ifdef _LIBCPP_VERSION
588 // This is why variant should SFINAE member swap. :-)
589 template class std::variant<int, NotSwappable>;
590 #endif
591 
non_constexpr_test()592 void non_constexpr_test() {
593   test_swap_valueless_by_exception();
594   test_swap_same_alternative_throws();
595   test_swap_different_alternatives_throws();
596 }
597 
test()598 TEST_CONSTEXPR_CXX20 bool test() {
599   test_swap_same_alternative();
600   test_swap_different_alternatives();
601   test_swap_sfinae();
602   test_swap_noexcept();
603 
604   return true;
605 }
606 
main(int,char **)607 int main(int, char**) {
608   non_constexpr_test();
609   test();
610 
611 #if TEST_STD_VER >= 20
612   static_assert(test());
613 #endif
614 
615   return 0;
616 }
617