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