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