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};
41constexpr void 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};
54constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {}
55
56template <bool Throws>
57constexpr void do_throw() {}
58
59template <>
60void do_throw<true>() {
61#ifndef TEST_HAS_NO_EXCEPTIONS
62 throw 42;
63#else
64 std::abort();
65#endif
66}
67
68template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap, bool EnableSwap = true>
69struct NothrowTypeImp {
70 int value;
71 int* move_called;
72 int* move_assign_called;
73 int* swap_called;
74
75 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
78 NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test
79
80 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
90 NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) {
91 assert(false);
92 return *this;
93 } // never called by the tests
94
95 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
104template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap>
105constexpr void
106swap(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
114using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
115// throwing copy and move assign, nothrow move ctor, no swap provided
116using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
117// nothrow move ctor, throwing move assignment, swap provided
118using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp<false, true, false, false, false, true>;
119// throwing move ctor, nothrow move assignment, no swap provided
120using ThrowingMoveCtor = NothrowTypeImp<false, false, false, true, false, false>;
121// throwing special members, nothrowing swap
122using ThrowingTypeWithNothrowSwap = NothrowTypeImp<false, false, false, false, true, true>;
123using NothrowTypeWithThrowingSwap = NothrowTypeImp<true, true, true, true, false, true>;
124// throwing move assign with nothrow move and nothrow swap
125using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp<false, true, false, false, true, true>;
126// throwing move assign with nothrow move but no swap.
127using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
128
129struct NonThrowingNonNoexceptType {
130 int value;
131 int* move_called;
132 constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {}
133 constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false)
134 : value(o.value), move_called(o.move_called) {
135 ++*move_called;
136 o.value = -1;
137 }
138 NonThrowingNonNoexceptType& operator=(NonThrowingNonNoexceptType&&) noexcept(false) {
139 assert(false); // never called by the tests.
140 return *this;
141 }
142};
143
144struct ThrowsOnSecondMove {
145 int value;
146 int move_count;
147 ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
148 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 }
153 ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) {
154 assert(false); // not called by test
155 return *this;
156 }
157};
158
159void 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
207TEST_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(rhs&: v2);
216 assert(swap_called == 1);
217 assert(std::get<0>(v1).value == 100);
218 assert(std::get<0>(v2).value == 42);
219 swap(lhs&: v1, rhs&: 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(rhs&: 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(lhs&: v1, rhs&: 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
254void test_swap_same_alternative_throws(){
255#ifndef TEST_HAS_NO_EXCEPTIONS
256 {using V = std::variant<NothrowTypeWithThrowingSwap, int>;
257int move_called = 0;
258int move_assign_called = 0;
259int swap_called = 0;
260V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
261V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
262try {
263 v1.swap(rhs&: v2);
264 assert(false);
265} catch (int) {
266}
267assert(swap_called == 1);
268assert(move_called == 0);
269assert(move_assign_called == 0);
270assert(std::get<0>(v1).value == 42);
271assert(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(rhs&: 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(rhs&: 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
313TEST_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(rhs&: 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(lhs&: v1, rhs&: 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
345void 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(rhs&: 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(rhs&: 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
478template <class Var>
479constexpr auto has_swap_member_imp(int) -> decltype(std::declval<Var&>().swap(std::declval<Var&>()), true) {
480 return true;
481}
482
483template <class Var>
484constexpr auto has_swap_member_imp(long) -> bool {
485 return false;
486}
487
488template <class Var>
489constexpr bool has_swap_member() {
490 return has_swap_member_imp<Var>(0);
491}
492
493constexpr 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
519TEST_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(rhs&: v2);
527 swap(lhs&: v1, rhs&: 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(rhs&: v2);
536 swap(lhs&: v1, rhs&: 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(rhs&: v2);
545 swap(lhs&: v1, rhs&: 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(rhs&: v2);
554 swap(lhs&: v1, rhs&: 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(rhs&: v2);
563 swap(lhs&: v1, rhs&: 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(rhs&: v2);
572 swap(lhs&: v1, rhs&: 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. :-)
589template class std::variant<int, NotSwappable>;
590#endif
591
592void non_constexpr_test() {
593 test_swap_valueless_by_exception();
594 test_swap_same_alternative_throws();
595 test_swap_different_alternatives_throws();
596}
597
598TEST_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
607int 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

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