brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.2 KiB · 157ff68 Raw
549 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// constexpr variant& operator=(variant&&) noexcept(see below);16 17#include <cassert>18#include <string>19#include <type_traits>20#include <utility>21#include <variant>22 23#include "test_macros.h"24#include "variant_test_helpers.h"25 26struct NoCopy {27  NoCopy(const NoCopy&)            = delete;28  NoCopy& operator=(const NoCopy&) = default;29};30 31struct CopyOnly {32  CopyOnly(const CopyOnly&)            = default;33  CopyOnly(CopyOnly&&)                 = delete;34  CopyOnly& operator=(const CopyOnly&) = default;35  CopyOnly& operator=(CopyOnly&&)      = delete;36};37 38struct MoveOnly {39  MoveOnly(const MoveOnly&)            = delete;40  MoveOnly(MoveOnly&&)                 = default;41  MoveOnly& operator=(const MoveOnly&) = delete;42  MoveOnly& operator=(MoveOnly&&)      = default;43};44 45struct MoveOnlyNT {46  MoveOnlyNT(const MoveOnlyNT&) = delete;47  MoveOnlyNT(MoveOnlyNT&&) {}48  MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;49  MoveOnlyNT& operator=(MoveOnlyNT&&)      = default;50};51 52struct MoveOnlyOddNothrow {53  MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}54  MoveOnlyOddNothrow(const MoveOnlyOddNothrow&)                = delete;55  MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;56  MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&)     = delete;57};58 59struct MoveAssignOnly {60  MoveAssignOnly(MoveAssignOnly&&)            = delete;61  MoveAssignOnly& operator=(MoveAssignOnly&&) = default;62};63 64struct MoveAssign {65  constexpr MoveAssign(int v, int* move_ctor, int* move_assi)66      : value(v), move_construct(move_ctor), move_assign(move_assi) {}67  constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) {68    ++*move_construct;69    o.value = -1;70  }71  constexpr MoveAssign& operator=(MoveAssign&& o) {72    value          = o.value;73    move_construct = o.move_construct;74    move_assign    = o.move_assign;75    ++*move_assign;76    o.value = -1;77    return *this;78  }79  int value;80  int* move_construct;81  int* move_assign;82};83 84struct NTMoveAssign {85  constexpr NTMoveAssign(int v) : value(v) {}86  NTMoveAssign(const NTMoveAssign&)                 = default;87  NTMoveAssign(NTMoveAssign&&)                      = default;88  NTMoveAssign& operator=(const NTMoveAssign& that) = default;89  NTMoveAssign& operator=(NTMoveAssign&& that) {90    value      = that.value;91    that.value = -1;92    return *this;93  };94  int value;95};96 97static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");98static_assert(std::is_move_assignable<NTMoveAssign>::value, "");99 100struct TMoveAssign {101  constexpr TMoveAssign(int v) : value(v) {}102  TMoveAssign(const TMoveAssign&)            = delete;103  TMoveAssign(TMoveAssign&&)                 = default;104  TMoveAssign& operator=(const TMoveAssign&) = delete;105  TMoveAssign& operator=(TMoveAssign&&)      = default;106  int value;107};108 109static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");110 111struct TMoveAssignNTCopyAssign {112  constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}113  TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;114  TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&)      = default;115  TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {116    value = that.value;117    return *this;118  }119  TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;120  int value;121};122 123static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");124 125struct TrivialCopyNontrivialMove {126  TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;127  TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}128  TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;129  TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }130};131 132static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");133static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");134 135constexpr void test_move_assignment_noexcept() {136  {137    using V = std::variant<int>;138    static_assert(std::is_nothrow_move_assignable<V>::value, "");139  }140  {141    using V = std::variant<MoveOnly>;142    static_assert(std::is_nothrow_move_assignable<V>::value, "");143  }144  {145    using V = std::variant<int, long>;146    static_assert(std::is_nothrow_move_assignable<V>::value, "");147  }148  {149    using V = std::variant<int, MoveOnly>;150    static_assert(std::is_nothrow_move_assignable<V>::value, "");151  }152  {153    using V = std::variant<MoveOnlyNT>;154    static_assert(!std::is_nothrow_move_assignable<V>::value, "");155  }156  {157    using V = std::variant<MoveOnlyOddNothrow>;158    static_assert(!std::is_nothrow_move_assignable<V>::value, "");159  }160}161 162constexpr void test_move_assignment_sfinae() {163  {164    using V = std::variant<int, long>;165    static_assert(std::is_move_assignable<V>::value, "");166  }167  {168    using V = std::variant<int, CopyOnly>;169    static_assert(std::is_move_assignable<V>::value, "");170  }171  {172    using V = std::variant<int, NoCopy>;173    static_assert(!std::is_move_assignable<V>::value, "");174  }175  {176    using V = std::variant<int, MoveOnly>;177    static_assert(std::is_move_assignable<V>::value, "");178  }179  {180    using V = std::variant<int, MoveOnlyNT>;181    static_assert(std::is_move_assignable<V>::value, "");182  }183  {184    // variant only provides move assignment when the types also provide185    // a move constructor.186    using V = std::variant<int, MoveAssignOnly>;187    static_assert(!std::is_move_assignable<V>::value, "");188  }189 190  // Make sure we properly propagate triviality (see P0602R4).191  {192    using V = std::variant<int, long>;193    static_assert(std::is_trivially_move_assignable<V>::value, "");194  }195  {196    using V = std::variant<int, NTMoveAssign>;197    static_assert(!std::is_trivially_move_assignable<V>::value, "");198    static_assert(std::is_move_assignable<V>::value, "");199  }200  {201    using V = std::variant<int, TMoveAssign>;202    static_assert(std::is_trivially_move_assignable<V>::value, "");203  }204  {205    using V = std::variant<int, TMoveAssignNTCopyAssign>;206    static_assert(std::is_trivially_move_assignable<V>::value, "");207  }208  {209    using V = std::variant<int, TrivialCopyNontrivialMove>;210    static_assert(!std::is_trivially_move_assignable<V>::value, "");211  }212  {213    using V = std::variant<int, CopyOnly>;214    static_assert(std::is_trivially_move_assignable<V>::value, "");215  }216}217 218void test_move_assignment_empty_empty() {219#ifndef TEST_HAS_NO_EXCEPTIONS220  using MET = MakeEmptyT;221  {222    using V = std::variant<int, long, MET>;223    V v1(std::in_place_index<0>);224    makeEmpty(v1);225    V v2(std::in_place_index<0>);226    makeEmpty(v2);227    V& vref = (v1 = std::move(v2));228    assert(&vref == &v1);229    assert(v1.valueless_by_exception());230    assert(v1.index() == std::variant_npos);231  }232#endif // TEST_HAS_NO_EXCEPTIONS233}234 235void test_move_assignment_non_empty_empty() {236#ifndef TEST_HAS_NO_EXCEPTIONS237  using MET = MakeEmptyT;238  {239    using V = std::variant<int, MET>;240    V v1(std::in_place_index<0>, 42);241    V v2(std::in_place_index<0>);242    makeEmpty(v2);243    V& vref = (v1 = std::move(v2));244    assert(&vref == &v1);245    assert(v1.valueless_by_exception());246    assert(v1.index() == std::variant_npos);247  }248  {249    using V = std::variant<int, MET, std::string>;250    V v1(std::in_place_index<2>, "hello");251    V v2(std::in_place_index<0>);252    makeEmpty(v2);253    V& vref = (v1 = std::move(v2));254    assert(&vref == &v1);255    assert(v1.valueless_by_exception());256    assert(v1.index() == std::variant_npos);257  }258#endif // TEST_HAS_NO_EXCEPTIONS259}260 261void test_move_assignment_empty_non_empty() {262#ifndef TEST_HAS_NO_EXCEPTIONS263  using MET = MakeEmptyT;264  {265    using V = std::variant<int, MET>;266    V v1(std::in_place_index<0>);267    makeEmpty(v1);268    V v2(std::in_place_index<0>, 42);269    V& vref = (v1 = std::move(v2));270    assert(&vref == &v1);271    assert(v1.index() == 0);272    assert(std::get<0>(v1) == 42);273  }274  {275    using V = std::variant<int, MET, std::string>;276    V v1(std::in_place_index<0>);277    makeEmpty(v1);278    V v2(std::in_place_type<std::string>, "hello");279    V& vref = (v1 = std::move(v2));280    assert(&vref == &v1);281    assert(v1.index() == 2);282    assert(std::get<2>(v1) == "hello");283  }284#endif // TEST_HAS_NO_EXCEPTIONS285}286 287template <typename T>288struct Result {289  std::size_t index;290  T value;291};292 293TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() {294  {295    using V = std::variant<int>;296    V v1(43);297    V v2(42);298    V& vref = (v1 = std::move(v2));299    assert(&vref == &v1);300    assert(v1.index() == 0);301    assert(std::get<0>(v1) == 42);302  }303  {304    using V = std::variant<int, long, unsigned>;305    V v1(43l);306    V v2(42l);307    V& vref = (v1 = std::move(v2));308    assert(&vref == &v1);309    assert(v1.index() == 1);310    assert(std::get<1>(v1) == 42);311  }312  {313    using V            = std::variant<int, MoveAssign, unsigned>;314    int move_construct = 0;315    int move_assign    = 0;316    V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);317    V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);318    V& vref = (v1 = std::move(v2));319    assert(&vref == &v1);320    assert(v1.index() == 1);321    assert(std::get<1>(v1).value == 42);322    assert(move_construct == 0);323    assert(move_assign == 1);324  }325 326  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).327  {328    struct {329      constexpr Result<int> operator()() const {330        using V = std::variant<int>;331        V v(43);332        V v2(42);333        v = std::move(v2);334        return {v.index(), std::get<0>(v)};335      }336    } test;337    constexpr auto result = test();338    static_assert(result.index == 0, "");339    static_assert(result.value == 42, "");340  }341  {342    struct {343      constexpr Result<long> operator()() const {344        using V = std::variant<int, long, unsigned>;345        V v(43l);346        V v2(42l);347        v = std::move(v2);348        return {v.index(), std::get<1>(v)};349      }350    } test;351    constexpr auto result = test();352    static_assert(result.index == 1, "");353    static_assert(result.value == 42l, "");354  }355  {356    struct {357      constexpr Result<int> operator()() const {358        using V = std::variant<int, TMoveAssign, unsigned>;359        V v(std::in_place_type<TMoveAssign>, 43);360        V v2(std::in_place_type<TMoveAssign>, 42);361        v = std::move(v2);362        return {v.index(), std::get<1>(v).value};363      }364    } test;365    constexpr auto result = test();366    static_assert(result.index == 1, "");367    static_assert(result.value == 42, "");368  }369}370 371TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() {372  {373    using V = std::variant<int, long, unsigned>;374    V v1(43);375    V v2(42l);376    V& vref = (v1 = std::move(v2));377    assert(&vref == &v1);378    assert(v1.index() == 1);379    assert(std::get<1>(v1) == 42);380  }381  {382    using V            = std::variant<int, MoveAssign, unsigned>;383    int move_construct = 0;384    int move_assign    = 0;385    V v1(std::in_place_type<unsigned>, 43u);386    V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);387    V& vref = (v1 = std::move(v2));388    assert(&vref == &v1);389    assert(v1.index() == 1);390    assert(std::get<1>(v1).value == 42);391    assert(move_construct == 1);392    assert(move_assign == 0);393  }394 395  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).396  {397    struct {398      constexpr Result<long> operator()() const {399        using V = std::variant<int, long, unsigned>;400        V v(43);401        V v2(42l);402        v = std::move(v2);403        return {v.index(), std::get<1>(v)};404      }405    } test;406    constexpr auto result = test();407    static_assert(result.index == 1, "");408    static_assert(result.value == 42l, "");409  }410  {411    struct {412      constexpr Result<long> operator()() const {413        using V = std::variant<int, TMoveAssign, unsigned>;414        V v(std::in_place_type<unsigned>, 43u);415        V v2(std::in_place_type<TMoveAssign>, 42);416        v = std::move(v2);417        return {v.index(), std::get<1>(v).value};418      }419    } test;420    constexpr auto result = test();421    static_assert(result.index == 1, "");422    static_assert(result.value == 42, "");423  }424}425 426void test_assignment_throw() {427#ifndef TEST_HAS_NO_EXCEPTIONS428  using MET = MakeEmptyT;429  // same index430  {431    using V = std::variant<int, MET, std::string>;432    V v1(std::in_place_type<MET>);433    MET& mref = std::get<1>(v1);434    V v2(std::in_place_type<MET>);435    try {436      v1 = std::move(v2);437      assert(false);438    } catch (...) {439    }440    assert(v1.index() == 1);441    assert(&std::get<1>(v1) == &mref);442  }443 444  // different indices445  {446    using V = std::variant<int, MET, std::string>;447    V v1(std::in_place_type<int>);448    V v2(std::in_place_type<MET>);449    try {450      v1 = std::move(v2);451      assert(false);452    } catch (...) {453    }454    assert(v1.valueless_by_exception());455    assert(v1.index() == std::variant_npos);456  }457  {458    using V = std::variant<int, MET, std::string>;459    V v1(std::in_place_type<MET>);460    V v2(std::in_place_type<std::string>, "hello");461    V& vref = (v1 = std::move(v2));462    assert(&vref == &v1);463    assert(v1.index() == 2);464    assert(std::get<2>(v1) == "hello");465  }466#endif // TEST_HAS_NO_EXCEPTIONS467}468 469template <std::size_t NewIdx, class T, class ValueType>470constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {471  using Variant = std::decay_t<T>;472  Variant v2(std::forward<ValueType>(new_value));473  const auto cp = v2;474  v             = std::move(v2);475  assert(v.index() == NewIdx);476  assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));477}478 479constexpr void test_constexpr_move_assignment_trivial() {480  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).481  using V = std::variant<long, void*, int>;482  static_assert(std::is_trivially_copyable<V>::value, "");483  static_assert(std::is_trivially_move_assignable<V>::value, "");484  test_constexpr_assign_imp<0>(V(42l), 101l);485  test_constexpr_assign_imp<0>(V(nullptr), 101l);486  test_constexpr_assign_imp<1>(V(42l), nullptr);487  test_constexpr_assign_imp<2>(V(42l), 101);488}489 490struct NonTrivialMoveAssign {491  int i = 0;492  constexpr NonTrivialMoveAssign(int ii) : i(ii) {}493  constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;494  constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}495  constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;496  constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {497    i = o.i;498    return *this;499  }500  TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;501  friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }502};503 504TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() {505  using V = std::variant<long, void*, NonTrivialMoveAssign>;506  static_assert(!std::is_trivially_copyable<V>::value);507  static_assert(!std::is_trivially_move_assignable<V>::value);508  test_constexpr_assign_imp<0>(V(42l), 101l);509  test_constexpr_assign_imp<0>(V(nullptr), 101l);510  test_constexpr_assign_imp<1>(V(42l), nullptr);511  test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5));512  test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5));513}514 515void non_constexpr_test() {516  test_move_assignment_empty_empty();517  test_move_assignment_non_empty_empty();518  test_move_assignment_empty_non_empty();519  test_assignment_throw();520}521 522constexpr bool cxx17_constexpr_test() {523  test_move_assignment_sfinae();524  test_move_assignment_noexcept();525  test_constexpr_move_assignment_trivial();526 527  return true;528}529 530TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {531  test_move_assignment_same_index();532  test_move_assignment_different_index();533  test_constexpr_move_assignment_non_trivial();534 535  return true;536}537 538int main(int, char**) {539  non_constexpr_test();540  cxx17_constexpr_test();541  cxx20_constexpr_test();542 543  static_assert(cxx17_constexpr_test());544#if TEST_STD_VER >= 20545  static_assert(cxx20_constexpr_test());546#endif547  return 0;548}549