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
27struct NotSwappable {};
28void swap(NotSwappable &, NotSwappable &) = delete;
29
30struct NotCopyable {
31 NotCopyable() = default;
32 NotCopyable(const NotCopyable &) = delete;
33 NotCopyable &operator=(const NotCopyable &) = delete;
34};
35
36struct NotCopyableWithSwap {
37 NotCopyableWithSwap() = default;
38 NotCopyableWithSwap(const NotCopyableWithSwap &) = delete;
39 NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete;
40};
41void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {}
42
43struct NotMoveAssignable {
44 NotMoveAssignable() = default;
45 NotMoveAssignable(NotMoveAssignable &&) = default;
46 NotMoveAssignable &operator=(NotMoveAssignable &&) = delete;
47};
48
49struct NotMoveAssignableWithSwap {
50 NotMoveAssignableWithSwap() = default;
51 NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default;
52 NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete;
53};
54void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {}
55
56template <bool Throws> void do_throw() {}
57
58template <> void do_throw<true>() {
59#ifndef TEST_HAS_NO_EXCEPTIONS
60 throw 42;
61#else
62 std::abort();
63#endif
64}
65
66template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
67 bool NT_Swap, bool EnableSwap = true>
68struct NothrowTypeImp {
69 static int move_called;
70 static int move_assign_called;
71 static int swap_called;
72 static void reset() { move_called = move_assign_called = swap_called = 0; }
73 NothrowTypeImp() = default;
74 explicit NothrowTypeImp(int v) : value(v) {}
75 NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) {
76 assert(false);
77 } // never called by test
78 NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) {
79 ++move_called;
80 do_throw<!NT_Move>();
81 o.value = -1;
82 }
83 NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) {
84 assert(false);
85 return *this;
86 } // never called by the tests
87 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};
96template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
97 bool NT_Swap, bool EnableSwap>
98int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
99 EnableSwap>::move_called = 0;
100template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
101 bool NT_Swap, bool EnableSwap>
102int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
103 EnableSwap>::move_assign_called = 0;
104template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
105 bool NT_Swap, bool EnableSwap>
106int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
107 EnableSwap>::swap_called = 0;
108
109template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
110 bool NT_Swap>
111void 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
123using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
124// throwing copy and move assign, nothrow move ctor, no swap provided
125using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
126// nothrow move ctor, throwing move assignment, swap provided
127using NothrowMoveCtorWithThrowingSwap =
128 NothrowTypeImp<false, true, false, false, false, true>;
129// throwing move ctor, nothrow move assignment, no swap provided
130using ThrowingMoveCtor =
131 NothrowTypeImp<false, false, false, true, false, false>;
132// throwing special members, nothrowing swap
133using ThrowingTypeWithNothrowSwap =
134 NothrowTypeImp<false, false, false, false, true, true>;
135using NothrowTypeWithThrowingSwap =
136 NothrowTypeImp<true, true, true, true, false, true>;
137// throwing move assign with nothrow move and nothrow swap
138using ThrowingMoveAssignNothrowMoveCtorWithSwap =
139 NothrowTypeImp<false, true, false, false, true, true>;
140// throwing move assign with nothrow move but no swap.
141using ThrowingMoveAssignNothrowMoveCtor =
142 NothrowTypeImp<false, true, false, false, false, false>;
143
144struct NonThrowingNonNoexceptType {
145 static int move_called;
146 static void reset() { move_called = 0; }
147 NonThrowingNonNoexceptType() = default;
148 NonThrowingNonNoexceptType(int v) : value(v) {}
149 NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false)
150 : value(o.value) {
151 ++move_called;
152 o.value = -1;
153 }
154 NonThrowingNonNoexceptType &
155 operator=(NonThrowingNonNoexceptType &&) noexcept(false) {
156 assert(false); // never called by the tests.
157 return *this;
158 }
159 int value;
160};
161int NonThrowingNonNoexceptType::move_called = 0;
162
163struct ThrowsOnSecondMove {
164 int value;
165 int move_count;
166 ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
167 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 }
173 ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) {
174 assert(false); // not called by test
175 return *this;
176 }
177};
178
179void 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
227void 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(rhs&: 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(lhs&: v1, rhs&: 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(rhs&: 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(lhs&: v1, rhs&: 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(rhs&: 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(rhs&: 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(rhs&: 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
317void 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(rhs&: 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(lhs&: v1, rhs&: 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(rhs&: 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(rhs&: 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
471template <class Var>
472constexpr auto has_swap_member_imp(int)
473 -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) {
474 return true;
475}
476
477template <class Var> constexpr auto has_swap_member_imp(long) -> bool {
478 return false;
479}
480
481template <class Var> constexpr bool has_swap_member() {
482 return has_swap_member_imp<Var>(0);
483}
484
485void 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
511void 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(rhs&: v2);
519 swap(lhs&: v1, rhs&: 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(rhs&: v2);
528 swap(lhs&: v1, rhs&: 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(rhs&: v2);
537 swap(lhs&: v1, rhs&: 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(rhs&: v2);
546 swap(lhs&: v1, rhs&: 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(rhs&: v2);
555 swap(lhs&: v1, rhs&: 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(rhs&: v2);
564 swap(lhs&: v1, rhs&: 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. :-)
581template class std::variant<int, NotSwappable>;
582#endif
583
584int 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

source code of libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp