libcxx

libcxx mirror with random patches
git clone https://git.neptards.moe/neptards/libcxx.git
Log | Files | Refs

swap.pass.cpp (18157B)


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