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