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