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