brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.0 KiB · 039a237 Raw
617 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410 11// <variant>12 13// template <class ...Types> class variant;14 15// void swap(variant& rhs) noexcept(see below)16 17#include <cassert>18#include <cstdlib>19#include <string>20#include <type_traits>21#include <variant>22 23#include "test_convertible.h"24#include "test_macros.h"25#include "variant_test_helpers.h"26 27struct NotSwappable {};28void swap(NotSwappable&, NotSwappable&) = delete;29 30struct NotCopyable {31  NotCopyable()                              = default;32  NotCopyable(const NotCopyable&)            = delete;33  NotCopyable& operator=(const NotCopyable&) = delete;34};35 36struct NotCopyableWithSwap {37  NotCopyableWithSwap()                                      = default;38  NotCopyableWithSwap(const NotCopyableWithSwap&)            = delete;39  NotCopyableWithSwap& operator=(const NotCopyableWithSwap&) = delete;40};41constexpr void swap(NotCopyableWithSwap&, NotCopyableWithSwap) {}42 43struct NotMoveAssignable {44  NotMoveAssignable()                               = default;45  NotMoveAssignable(NotMoveAssignable&&)            = default;46  NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;47};48 49struct NotMoveAssignableWithSwap {50  NotMoveAssignableWithSwap()                                       = default;51  NotMoveAssignableWithSwap(NotMoveAssignableWithSwap&&)            = default;52  NotMoveAssignableWithSwap& operator=(NotMoveAssignableWithSwap&&) = delete;53};54constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {}55 56template <bool Throws>57constexpr void do_throw() {}58 59template <>60void do_throw<true>() {61#ifndef TEST_HAS_NO_EXCEPTIONS62  throw 42;63#else64  std::abort();65#endif66}67 68template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap, bool EnableSwap = true>69struct NothrowTypeImp {70  int value;71  int* move_called;72  int* move_assign_called;73  int* swap_called;74 75  constexpr NothrowTypeImp(int v, int* mv_ctr, int* mv_assign, int* swap)76      : value(v), move_called(mv_ctr), move_assign_called(mv_assign), swap_called(swap) {}77 78  NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test79 80  constexpr NothrowTypeImp(NothrowTypeImp&& o) noexcept(NT_Move)81      : value(o.value),82        move_called(o.move_called),83        move_assign_called(o.move_assign_called),84        swap_called(o.swap_called) {85    ++*move_called;86    do_throw<!NT_Move>();87    o.value = -1;88  }89 90  NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) {91    assert(false);92    return *this;93  } // never called by the tests94 95  constexpr 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};103 104template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap>105constexpr void106swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& lhs,107     NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& rhs) noexcept(NT_Swap) {108  ++*lhs.swap_called;109  do_throw<!NT_Swap>();110  std::swap(lhs.value, rhs.value);111}112 113// throwing copy, nothrow move ctor/assign, no swap provided114using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;115// throwing copy and move assign, nothrow move ctor, no swap provided116using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;117// nothrow move ctor, throwing move assignment, swap provided118using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp<false, true, false, false, false, true>;119// throwing move ctor, nothrow move assignment, no swap provided120using ThrowingMoveCtor = NothrowTypeImp<false, false, false, true, false, false>;121// throwing special members, nothrowing swap122using ThrowingTypeWithNothrowSwap = NothrowTypeImp<false, false, false, false, true, true>;123using NothrowTypeWithThrowingSwap = NothrowTypeImp<true, true, true, true, false, true>;124// throwing move assign with nothrow move and nothrow swap125using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp<false, true, false, false, true, true>;126// throwing move assign with nothrow move but no swap.127using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;128 129struct NonThrowingNonNoexceptType {130  int value;131  int* move_called;132  constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {}133  constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false)134      : value(o.value), move_called(o.move_called) {135    ++*move_called;136    o.value = -1;137  }138  NonThrowingNonNoexceptType& operator=(NonThrowingNonNoexceptType&&) noexcept(false) {139    assert(false); // never called by the tests.140    return *this;141  }142};143 144struct ThrowsOnSecondMove {145  int value;146  int move_count;147  ThrowsOnSecondMove(int v) : value(v), move_count(0) {}148  ThrowsOnSecondMove(ThrowsOnSecondMove&& o) noexcept(false) : value(o.value), move_count(o.move_count + 1) {149    if (move_count == 2)150      do_throw<true>();151    o.value = -1;152  }153  ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) {154    assert(false); // not called by test155    return *this;156  }157};158 159void test_swap_valueless_by_exception() {160#ifndef TEST_HAS_NO_EXCEPTIONS161  using V = std::variant<int, MakeEmptyT>;162  { // both empty163    V v1;164    makeEmpty(v1);165    V v2;166    makeEmpty(v2);167    assert(MakeEmptyT::alive == 0);168    { // member swap169      v1.swap(v2);170      assert(v1.valueless_by_exception());171      assert(v2.valueless_by_exception());172      assert(MakeEmptyT::alive == 0);173    }174    { // non-member swap175      swap(v1, v2);176      assert(v1.valueless_by_exception());177      assert(v2.valueless_by_exception());178      assert(MakeEmptyT::alive == 0);179    }180  }181  { // only one empty182    V v1(42);183    V v2;184    makeEmpty(v2);185    { // member swap186      v1.swap(v2);187      assert(v1.valueless_by_exception());188      assert(std::get<0>(v2) == 42);189      // swap again190      v2.swap(v1);191      assert(v2.valueless_by_exception());192      assert(std::get<0>(v1) == 42);193    }194    { // non-member swap195      swap(v1, v2);196      assert(v1.valueless_by_exception());197      assert(std::get<0>(v2) == 42);198      // swap again199      swap(v1, v2);200      assert(v2.valueless_by_exception());201      assert(std::get<0>(v1) == 42);202    }203  }204#endif205}206 207TEST_CONSTEXPR_CXX20 void test_swap_same_alternative() {208  {209    using V                = std::variant<ThrowingTypeWithNothrowSwap, int>;210    int move_called        = 0;211    int move_assign_called = 0;212    int swap_called        = 0;213    V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);214    V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);215    v1.swap(v2);216    assert(swap_called == 1);217    assert(std::get<0>(v1).value == 100);218    assert(std::get<0>(v2).value == 42);219    swap(v1, v2);220    assert(swap_called == 2);221    assert(std::get<0>(v1).value == 42);222    assert(std::get<0>(v2).value == 100);223 224    assert(move_called == 0);225    assert(move_assign_called == 0);226  }227  {228    using V                = std::variant<NothrowMoveable, int>;229    int move_called        = 0;230    int move_assign_called = 0;231    int swap_called        = 0;232    V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);233    V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);234    v1.swap(v2);235    assert(swap_called == 0);236    assert(move_called == 1);237    assert(move_assign_called == 2);238    assert(std::get<0>(v1).value == 100);239    assert(std::get<0>(v2).value == 42);240 241    move_called        = 0;242    move_assign_called = 0;243    swap_called        = 0;244 245    swap(v1, v2);246    assert(swap_called == 0);247    assert(move_called == 1);248    assert(move_assign_called == 2);249    assert(std::get<0>(v1).value == 42);250    assert(std::get<0>(v2).value == 100);251  }252}253 254void test_swap_same_alternative_throws(){255#ifndef TEST_HAS_NO_EXCEPTIONS256    {using V = std::variant<NothrowTypeWithThrowingSwap, int>;257int move_called        = 0;258int move_assign_called = 0;259int swap_called        = 0;260V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);261V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);262try {263  v1.swap(v2);264  assert(false);265} catch (int) {266}267assert(swap_called == 1);268assert(move_called == 0);269assert(move_assign_called == 0);270assert(std::get<0>(v1).value == 42);271assert(std::get<0>(v2).value == 100);272}273 274{275  using V                = std::variant<ThrowingMoveCtor, int>;276  int move_called        = 0;277  int move_assign_called = 0;278  int swap_called        = 0;279  V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);280  V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);281  try {282    v1.swap(v2);283    assert(false);284  } catch (int) {285  }286  assert(move_called == 1); // call threw287  assert(move_assign_called == 0);288  assert(swap_called == 0);289  assert(std::get<0>(v1).value == 42); // throw happened before v1 was moved from290  assert(std::get<0>(v2).value == 100);291}292{293  using V                = std::variant<ThrowingMoveAssignNothrowMoveCtor, int>;294  int move_called        = 0;295  int move_assign_called = 0;296  int swap_called        = 0;297  V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);298  V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);299  try {300    v1.swap(v2);301    assert(false);302  } catch (int) {303  }304  assert(move_called == 1);305  assert(move_assign_called == 1); // call threw and didn't complete306  assert(swap_called == 0);307  assert(std::get<0>(v1).value == -1); // v1 was moved from308  assert(std::get<0>(v2).value == 100);309}310#endif311}312 313TEST_CONSTEXPR_CXX20 void test_swap_different_alternatives() {314  {315    using V                = std::variant<NothrowMoveCtorWithThrowingSwap, int>;316    int move_called        = 0;317    int move_assign_called = 0;318    int swap_called        = 0;319    V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);320    V v2(std::in_place_index<1>, 100);321    v1.swap(v2);322    assert(swap_called == 0);323    // The libc++ implementation double copies the argument, and not324    // the variant swap is called on.325    LIBCPP_ASSERT(move_called == 1);326    assert(move_called <= 2);327    assert(move_assign_called == 0);328    assert(std::get<1>(v1) == 100);329    assert(std::get<0>(v2).value == 42);330 331    move_called        = 0;332    move_assign_called = 0;333    swap_called        = 0;334 335    swap(v1, v2);336    assert(swap_called == 0);337    LIBCPP_ASSERT(move_called == 2);338    assert(move_called <= 2);339    assert(move_assign_called == 0);340    assert(std::get<0>(v1).value == 42);341    assert(std::get<1>(v2) == 100);342  }343}344 345void test_swap_different_alternatives_throws() {346#ifndef TEST_HAS_NO_EXCEPTIONS347  {348    using V                 = std::variant<ThrowingTypeWithNothrowSwap, NonThrowingNonNoexceptType>;349    int move_called1        = 0;350    int move_assign_called1 = 0;351    int swap_called1        = 0;352    int move_called2        = 0;353    V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);354    V v2(std::in_place_index<1>, 100, &move_called2);355    try {356      v1.swap(v2);357      assert(false);358    } catch (int) {359    }360    assert(swap_called1 == 0);361    assert(move_called1 == 1); // throws362    assert(move_assign_called1 == 0);363    // FIXME: libc++ shouldn't move from T2 here.364    LIBCPP_ASSERT(move_called2 == 1);365    assert(move_called2 <= 1);366    assert(std::get<0>(v1).value == 42);367    if (move_called2 != 0)368      assert(v2.valueless_by_exception());369    else370      assert(std::get<1>(v2).value == 100);371  }372  {373    using V                 = std::variant<NonThrowingNonNoexceptType, ThrowingTypeWithNothrowSwap>;374    int move_called1        = 0;375    int move_called2        = 0;376    int move_assign_called2 = 0;377    int swap_called2        = 0;378    V v1(std::in_place_index<0>, 42, &move_called1);379    V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);380    try {381      v1.swap(v2);382      assert(false);383    } catch (int) {384    }385    LIBCPP_ASSERT(move_called1 == 0);386    assert(move_called1 <= 1);387    assert(swap_called2 == 0);388    assert(move_called2 == 1); // throws389    assert(move_assign_called2 == 0);390    if (move_called1 != 0)391      assert(v1.valueless_by_exception());392    else393      assert(std::get<0>(v1).value == 42);394    assert(std::get<1>(v2).value == 100);395  }396// FIXME: The tests below are just very libc++ specific397#  ifdef _LIBCPP_VERSION398  {399    using V         = std::variant<ThrowsOnSecondMove, NonThrowingNonNoexceptType>;400    int move_called = 0;401    V v1(std::in_place_index<0>, 42);402    V v2(std::in_place_index<1>, 100, &move_called);403    v1.swap(v2);404    assert(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 V         = std::variant<NonThrowingNonNoexceptType, ThrowsOnSecondMove>;411    int move_called = 0;412    V v1(std::in_place_index<0>, 42, &move_called);413    V v2(std::in_place_index<1>, 100);414    try {415      v1.swap(v2);416      assert(false);417    } catch (int) {418    }419    assert(move_called == 1);420    assert(v1.valueless_by_exception());421    assert(std::get<0>(v2).value == 42);422  }423#  endif424  // testing libc++ extension. If either variant stores a nothrow move425  // constructible type v1.swap(v2) provides the strong exception safety426  // guarantee.427#  ifdef _LIBCPP_VERSION428  {429    using V                 = std::variant<ThrowingTypeWithNothrowSwap, NothrowMoveable>;430    int move_called1        = 0;431    int move_assign_called1 = 0;432    int swap_called1        = 0;433    int move_called2        = 0;434    int move_assign_called2 = 0;435    int swap_called2        = 0;436    V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);437    V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);438    try {439      v1.swap(v2);440      assert(false);441    } catch (int) {442    }443    assert(swap_called1 == 0);444    assert(move_called1 == 1);445    assert(move_assign_called1 == 0);446    assert(swap_called2 == 0);447    assert(move_called2 == 2);448    assert(move_assign_called2 == 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 453    move_called1        = 0;454    move_assign_called1 = 0;455    swap_called1        = 0;456    move_called2        = 0;457    move_assign_called2 = 0;458    swap_called2        = 0;459 460    try {461      v2.swap(v1);462      assert(false);463    } catch (int) {464    }465    assert(swap_called1 == 0);466    assert(move_called1 == 1);467    assert(move_assign_called1 == 0);468    assert(swap_called2 == 0);469    assert(move_called2 == 2);470    assert(move_assign_called2 == 0);471    assert(std::get<0>(v1).value == 42);472    assert(std::get<1>(v2).value == 100);473  }474#  endif // _LIBCPP_VERSION475#endif476}477 478template <class Var>479constexpr auto has_swap_member_imp(int) -> decltype(std::declval<Var&>().swap(std::declval<Var&>()), true) {480  return true;481}482 483template <class Var>484constexpr auto has_swap_member_imp(long) -> bool {485  return false;486}487 488template <class Var>489constexpr bool has_swap_member() {490  return has_swap_member_imp<Var>(0);491}492 493constexpr void test_swap_sfinae() {494  {495    // This variant type does not provide either a member or non-member swap496    // but is still swappable via the generic swap algorithm, since the497    // 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 519TEST_CONSTEXPR_CXX20 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 swap525    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 swap534    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 swap543    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 swap552    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 swap561    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 swap570    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 swap576    // but is still swappable via the generic swap algorithm, since the577    // 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_VERSION588// This is why variant should SFINAE member swap. :-)589template class std::variant<int, NotSwappable>;590#endif591 592void non_constexpr_test() {593  test_swap_valueless_by_exception();594  test_swap_same_alternative_throws();595  test_swap_different_alternatives_throws();596}597 598TEST_CONSTEXPR_CXX20 bool test() {599  test_swap_same_alternative();600  test_swap_different_alternatives();601  test_swap_sfinae();602  test_swap_noexcept();603 604  return true;605}606 607int main(int, char**) {608  non_constexpr_test();609  test();610 611#if TEST_STD_VER >= 20612  static_assert(test());613#endif614 615  return 0;616}617