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
28struct NotSwappable {};
29void swap(NotSwappable &, NotSwappable &) = delete;
30
31struct NotCopyable {
32  NotCopyable() = default;
33  NotCopyable(const NotCopyable &) = delete;
34  NotCopyable &operator=(const NotCopyable &) = delete;
35};
36
37struct NotCopyableWithSwap {
38  NotCopyableWithSwap() = default;
39  NotCopyableWithSwap(const NotCopyableWithSwap &) = delete;
40  NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete;
41};
42void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {}
43
44struct NotMoveAssignable {
45  NotMoveAssignable() = default;
46  NotMoveAssignable(NotMoveAssignable &&) = default;
47  NotMoveAssignable &operator=(NotMoveAssignable &&) = delete;
48};
49
50struct NotMoveAssignableWithSwap {
51  NotMoveAssignableWithSwap() = default;
52  NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default;
53  NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete;
54};
55void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {}
56
57template <bool Throws> void do_throw() {}
58
59template <> void do_throw<true>() {
60#ifndef TEST_HAS_NO_EXCEPTIONS
61  throw 42;
62#else
63  std::abort();
64#endif
65}
66
67template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
68          bool NT_Swap, bool EnableSwap = true>
69struct NothrowTypeImp {
70  static int move_called;
71  static int move_assign_called;
72  static int swap_called;
73  static void reset() { move_called = move_assign_called = swap_called = 0; }
74  NothrowTypeImp() = default;
75  explicit NothrowTypeImp(int v) : value(v) {}
76  NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) {
77    assert(false);
78  } // never called by test
79  NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) {
80    ++move_called;
81    do_throw<!NT_Move>();
82    o.value = -1;
83  }
84  NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) {
85    assert(false);
86    return *this;
87  } // never called by the tests
88  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};
97template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
98          bool NT_Swap, bool EnableSwap>
99int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
100                   EnableSwap>::move_called = 0;
101template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
102          bool NT_Swap, bool EnableSwap>
103int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
104                   EnableSwap>::move_assign_called = 0;
105template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
106          bool NT_Swap, bool EnableSwap>
107int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
108                   EnableSwap>::swap_called = 0;
109
110template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
111          bool NT_Swap>
112void 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
124using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
125// throwing copy and move assign, nothrow move ctor, no swap provided
126using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
127// nothrow move ctor, throwing move assignment, swap provided
128using NothrowMoveCtorWithThrowingSwap =
129    NothrowTypeImp<false, true, false, false, false, true>;
130// throwing move ctor, nothrow move assignment, no swap provided
131using ThrowingMoveCtor =
132    NothrowTypeImp<false, false, false, true, false, false>;
133// throwing special members, nothrowing swap
134using ThrowingTypeWithNothrowSwap =
135    NothrowTypeImp<false, false, false, false, true, true>;
136using NothrowTypeWithThrowingSwap =
137    NothrowTypeImp<true, true, true, true, false, true>;
138// throwing move assign with nothrow move and nothrow swap
139using ThrowingMoveAssignNothrowMoveCtorWithSwap =
140    NothrowTypeImp<false, true, false, false, true, true>;
141// throwing move assign with nothrow move but no swap.
142using ThrowingMoveAssignNothrowMoveCtor =
143    NothrowTypeImp<false, true, false, false, false, false>;
144
145struct NonThrowingNonNoexceptType {
146  static int move_called;
147  static void reset() { move_called = 0; }
148  NonThrowingNonNoexceptType() = default;
149  NonThrowingNonNoexceptType(int v) : value(v) {}
150  NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false)
151      : value(o.value) {
152    ++move_called;
153    o.value = -1;
154  }
155  NonThrowingNonNoexceptType &
156  operator=(NonThrowingNonNoexceptType &&) noexcept(false) {
157    assert(false); // never called by the tests.
158    return *this;
159  }
160  int value;
161};
162int NonThrowingNonNoexceptType::move_called = 0;
163
164struct ThrowsOnSecondMove {
165  int value;
166  int move_count;
167  ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
168  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  }
174  ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) {
175    assert(false); // not called by test
176    return *this;
177  }
178};
179
180void 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
228void 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
318void 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
472template <class Var>
473constexpr auto has_swap_member_imp(int)
474    -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) {
475  return true;
476}
477
478template <class Var> constexpr auto has_swap_member_imp(long) -> bool {
479  return false;
480}
481
482template <class Var> constexpr bool has_swap_member() {
483  return has_swap_member_imp<Var>(0);
484}
485
486void 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
512void 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. :-)
582template class std::variant<int, NotSwappable>;
583#endif
584
585int 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