brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.2 KiB · a6d3f34 Raw
668 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 const&);16 17#include <cassert>18#include <string>19#include <type_traits>20#include <variant>21 22#include "test_macros.h"23 24struct NoCopy {25  NoCopy(const NoCopy&)            = delete;26  NoCopy& operator=(const NoCopy&) = default;27};28 29struct CopyOnly {30  CopyOnly(const CopyOnly&)            = default;31  CopyOnly(CopyOnly&&)                 = delete;32  CopyOnly& operator=(const CopyOnly&) = default;33  CopyOnly& operator=(CopyOnly&&)      = delete;34};35 36struct MoveOnly {37  MoveOnly(const MoveOnly&)            = delete;38  MoveOnly(MoveOnly&&)                 = default;39  MoveOnly& operator=(const MoveOnly&) = default;40};41 42struct MoveOnlyNT {43  MoveOnlyNT(const MoveOnlyNT&) = delete;44  MoveOnlyNT(MoveOnlyNT&&) {}45  MoveOnlyNT& operator=(const MoveOnlyNT&) = default;46};47 48struct CopyAssign {49  constexpr CopyAssign(int v, int* alv, int* cpy_ctr, int* cpy_assi, int* move_ctr, int* move_assi)50      : value(v),51        alive(alv),52        copy_construct(cpy_ctr),53        copy_assign(cpy_assi),54        move_construct(move_ctr),55        move_assign(move_assi) {56    ++*alive;57  }58  constexpr CopyAssign(const CopyAssign& o)59      : value(o.value),60        alive(o.alive),61        copy_construct(o.copy_construct),62        copy_assign(o.copy_assign),63        move_construct(o.move_construct),64        move_assign(o.move_assign) {65    ++*alive;66    ++*copy_construct;67  }68  constexpr CopyAssign(CopyAssign&& o) noexcept69      : value(o.value),70        alive(o.alive),71        copy_construct(o.copy_construct),72        copy_assign(o.copy_assign),73        move_construct(o.move_construct),74        move_assign(o.move_assign) {75    o.value = -1;76    ++*alive;77    ++*move_construct;78  }79  constexpr CopyAssign& operator=(const CopyAssign& o) {80    value          = o.value;81    alive          = o.alive;82    copy_construct = o.copy_construct;83    copy_assign    = o.copy_assign;84    move_construct = o.move_construct;85    move_assign    = o.move_assign;86    ++*copy_assign;87    return *this;88  }89  constexpr CopyAssign& operator=(CopyAssign&& o) noexcept {90    value          = o.value;91    alive          = o.alive;92    copy_construct = o.copy_construct;93    copy_assign    = o.copy_assign;94    move_construct = o.move_construct;95    move_assign    = o.move_assign;96    o.value        = -1;97    ++*move_assign;98    return *this;99  }100  TEST_CONSTEXPR_CXX20 ~CopyAssign() { --*alive; }101  int value;102  int* alive;103  int* copy_construct;104  int* copy_assign;105  int* move_construct;106  int* move_assign;107};108 109struct CopyMaybeThrows {110  CopyMaybeThrows(const CopyMaybeThrows&);111  CopyMaybeThrows& operator=(const CopyMaybeThrows&);112};113struct CopyDoesThrow {114  CopyDoesThrow(const CopyDoesThrow&) noexcept(false);115  CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false);116};117 118struct NTCopyAssign {119  constexpr NTCopyAssign(int v) : value(v) {}120  NTCopyAssign(const NTCopyAssign&) = default;121  NTCopyAssign(NTCopyAssign&&)      = default;122  NTCopyAssign& operator=(const NTCopyAssign& that) {123    value = that.value;124    return *this;125  };126  NTCopyAssign& operator=(NTCopyAssign&&) = delete;127  int value;128};129 130static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");131static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");132 133struct TCopyAssign {134  constexpr TCopyAssign(int v) : value(v) {}135  TCopyAssign(const TCopyAssign&)            = default;136  TCopyAssign(TCopyAssign&&)                 = default;137  TCopyAssign& operator=(const TCopyAssign&) = default;138  TCopyAssign& operator=(TCopyAssign&&)      = delete;139  int value;140};141 142static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");143 144struct TCopyAssignNTMoveAssign {145  constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}146  TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&)            = default;147  TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&)                 = default;148  TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default;149  TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) {150    value      = that.value;151    that.value = -1;152    return *this;153  }154  int value;155};156 157static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");158 159#ifndef TEST_HAS_NO_EXCEPTIONS160struct CopyThrows {161  CopyThrows() = default;162  CopyThrows(const CopyThrows&) { throw 42; }163  CopyThrows& operator=(const CopyThrows&) { throw 42; }164};165 166struct CopyCannotThrow {167  static int alive;168  CopyCannotThrow() { ++alive; }169  CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; }170  CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); }171  CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default;172  CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept {173    assert(false);174    return *this;175  }176};177 178int CopyCannotThrow::alive = 0;179 180struct MoveThrows {181  static int alive;182  MoveThrows() { ++alive; }183  MoveThrows(const MoveThrows&) { ++alive; }184  MoveThrows(MoveThrows&&) { throw 42; }185  MoveThrows& operator=(const MoveThrows&) { return *this; }186  MoveThrows& operator=(MoveThrows&&) { throw 42; }187  ~MoveThrows() { --alive; }188};189 190int MoveThrows::alive = 0;191 192struct MakeEmptyT {193  static int alive;194  MakeEmptyT() { ++alive; }195  MakeEmptyT(const MakeEmptyT&) {196    ++alive;197    // Don't throw from the copy constructor since variant's assignment198    // operator performs a copy before committing to the assignment.199  }200  MakeEmptyT(MakeEmptyT&&) { throw 42; }201  MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }202  MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }203  ~MakeEmptyT() { --alive; }204};205 206int MakeEmptyT::alive = 0;207 208template <class Variant>209void makeEmpty(Variant& v) {210  Variant v2(std::in_place_type<MakeEmptyT>);211  try {212    v = std::move(v2);213    assert(false);214  } catch (...) {215    assert(v.valueless_by_exception());216  }217}218#endif // TEST_HAS_NO_EXCEPTIONS219 220constexpr void test_copy_assignment_not_noexcept() {221  {222    using V = std::variant<CopyMaybeThrows>;223    static_assert(!std::is_nothrow_copy_assignable<V>::value, "");224  }225  {226    using V = std::variant<int, CopyDoesThrow>;227    static_assert(!std::is_nothrow_copy_assignable<V>::value, "");228  }229}230 231constexpr void test_copy_assignment_sfinae() {232  {233    using V = std::variant<int, long>;234    static_assert(std::is_copy_assignable<V>::value, "");235  }236  {237    using V = std::variant<int, CopyOnly>;238    static_assert(std::is_copy_assignable<V>::value, "");239  }240  {241    using V = std::variant<int, NoCopy>;242    static_assert(!std::is_copy_assignable<V>::value, "");243  }244  {245    using V = std::variant<int, MoveOnly>;246    static_assert(!std::is_copy_assignable<V>::value, "");247  }248  {249    using V = std::variant<int, MoveOnlyNT>;250    static_assert(!std::is_copy_assignable<V>::value, "");251  }252 253  // Make sure we properly propagate triviality (see P0602R4).254  {255    using V = std::variant<int, long>;256    static_assert(std::is_trivially_copy_assignable<V>::value, "");257  }258  {259    using V = std::variant<int, NTCopyAssign>;260    static_assert(!std::is_trivially_copy_assignable<V>::value, "");261    static_assert(std::is_copy_assignable<V>::value, "");262  }263  {264    using V = std::variant<int, TCopyAssign>;265    static_assert(std::is_trivially_copy_assignable<V>::value, "");266  }267  {268    using V = std::variant<int, TCopyAssignNTMoveAssign>;269    static_assert(std::is_trivially_copy_assignable<V>::value, "");270  }271  {272    using V = std::variant<int, CopyOnly>;273    static_assert(std::is_trivially_copy_assignable<V>::value, "");274  }275}276 277void test_copy_assignment_empty_empty() {278#ifndef TEST_HAS_NO_EXCEPTIONS279  using MET = MakeEmptyT;280  {281    using V = std::variant<int, long, MET>;282    V v1(std::in_place_index<0>);283    makeEmpty(v1);284    V v2(std::in_place_index<0>);285    makeEmpty(v2);286    V& vref = (v1 = v2);287    assert(&vref == &v1);288    assert(v1.valueless_by_exception());289    assert(v1.index() == std::variant_npos);290  }291#endif // TEST_HAS_NO_EXCEPTIONS292}293 294void test_copy_assignment_non_empty_empty() {295#ifndef TEST_HAS_NO_EXCEPTIONS296  using MET = MakeEmptyT;297  {298    using V = std::variant<int, MET>;299    V v1(std::in_place_index<0>, 42);300    V v2(std::in_place_index<0>);301    makeEmpty(v2);302    V& vref = (v1 = v2);303    assert(&vref == &v1);304    assert(v1.valueless_by_exception());305    assert(v1.index() == std::variant_npos);306  }307  {308    using V = std::variant<int, MET, std::string>;309    V v1(std::in_place_index<2>, "hello");310    V v2(std::in_place_index<0>);311    makeEmpty(v2);312    V& vref = (v1 = v2);313    assert(&vref == &v1);314    assert(v1.valueless_by_exception());315    assert(v1.index() == std::variant_npos);316  }317#endif // TEST_HAS_NO_EXCEPTIONS318}319 320void test_copy_assignment_empty_non_empty() {321#ifndef TEST_HAS_NO_EXCEPTIONS322  using MET = MakeEmptyT;323  {324    using V = std::variant<int, MET>;325    V v1(std::in_place_index<0>);326    makeEmpty(v1);327    V v2(std::in_place_index<0>, 42);328    V& vref = (v1 = v2);329    assert(&vref == &v1);330    assert(v1.index() == 0);331    assert(std::get<0>(v1) == 42);332  }333  {334    using V = std::variant<int, MET, std::string>;335    V v1(std::in_place_index<0>);336    makeEmpty(v1);337    V v2(std::in_place_type<std::string>, "hello");338    V& vref = (v1 = v2);339    assert(&vref == &v1);340    assert(v1.index() == 2);341    assert(std::get<2>(v1) == "hello");342  }343#endif // TEST_HAS_NO_EXCEPTIONS344}345 346template <typename T>347struct Result {348  std::size_t index;349  T value;350};351 352TEST_CONSTEXPR_CXX20 void test_copy_assignment_same_index() {353  {354    using V = std::variant<int>;355    V v1(43);356    V v2(42);357    V& vref = (v1 = v2);358    assert(&vref == &v1);359    assert(v1.index() == 0);360    assert(std::get<0>(v1) == 42);361  }362  {363    using V = std::variant<int, long, unsigned>;364    V v1(43l);365    V v2(42l);366    V& vref = (v1 = v2);367    assert(&vref == &v1);368    assert(v1.index() == 1);369    assert(std::get<1>(v1) == 42);370  }371  {372    using V            = std::variant<int, CopyAssign, unsigned>;373    int alive          = 0;374    int copy_construct = 0;375    int copy_assign    = 0;376    int move_construct = 0;377    int move_assign    = 0;378    V v1(std::in_place_type<CopyAssign>, 43, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);379    V v2(std::in_place_type<CopyAssign>, 42, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);380    V& vref = (v1 = v2);381    assert(&vref == &v1);382    assert(v1.index() == 1);383    assert(std::get<1>(v1).value == 42);384    assert(copy_construct == 0);385    assert(move_construct == 0);386    assert(copy_assign == 1);387  }388 389  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).390  {391    struct {392      constexpr Result<int> operator()() const {393        using V = std::variant<int>;394        V v(43);395        V v2(42);396        v = v2;397        return {v.index(), std::get<0>(v)};398      }399    } test;400    constexpr auto result = test();401    static_assert(result.index == 0, "");402    static_assert(result.value == 42, "");403  }404  {405    struct {406      constexpr Result<long> operator()() const {407        using V = std::variant<int, long, unsigned>;408        V v(43l);409        V v2(42l);410        v = v2;411        return {v.index(), std::get<1>(v)};412      }413    } test;414    constexpr auto result = test();415    static_assert(result.index == 1, "");416    static_assert(result.value == 42l, "");417  }418  {419    struct {420      constexpr Result<int> operator()() const {421        using V = std::variant<int, TCopyAssign, unsigned>;422        V v(std::in_place_type<TCopyAssign>, 43);423        V v2(std::in_place_type<TCopyAssign>, 42);424        v = v2;425        return {v.index(), std::get<1>(v).value};426      }427    } test;428    constexpr auto result = test();429    static_assert(result.index == 1, "");430    static_assert(result.value == 42, "");431  }432  {433    struct {434      constexpr Result<int> operator()() const {435        using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;436        V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);437        V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);438        v = v2;439        return {v.index(), std::get<1>(v).value};440      }441    } test;442    constexpr auto result = test();443    static_assert(result.index == 1, "");444    static_assert(result.value == 42, "");445  }446}447 448TEST_CONSTEXPR_CXX20 void test_copy_assignment_different_index() {449  {450    using V = std::variant<int, long, unsigned>;451    V v1(43);452    V v2(42l);453    V& vref = (v1 = v2);454    assert(&vref == &v1);455    assert(v1.index() == 1);456    assert(std::get<1>(v1) == 42);457  }458  {459    using V            = std::variant<int, CopyAssign, unsigned>;460    int alive          = 0;461    int copy_construct = 0;462    int copy_assign    = 0;463    int move_construct = 0;464    int move_assign    = 0;465    V v1(std::in_place_type<unsigned>, 43u);466    V v2(std::in_place_type<CopyAssign>, 42, &alive, &copy_construct, &copy_assign, &move_construct, &move_assign);467    assert(copy_construct == 0);468    assert(move_construct == 0);469    assert(alive == 1);470    V& vref = (v1 = v2);471    assert(&vref == &v1);472    assert(v1.index() == 1);473    assert(std::get<1>(v1).value == 42);474    assert(alive == 2);475    assert(copy_construct == 1);476    assert(move_construct == 1);477    assert(copy_assign == 0);478  }479 480  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).481  {482    struct {483      constexpr Result<long> operator()() const {484        using V = std::variant<int, long, unsigned>;485        V v(43);486        V v2(42l);487        v = v2;488        return {v.index(), std::get<1>(v)};489      }490    } test;491    constexpr auto result = test();492    static_assert(result.index == 1, "");493    static_assert(result.value == 42l, "");494  }495  {496    struct {497      constexpr Result<int> operator()() const {498        using V = std::variant<int, TCopyAssign, unsigned>;499        V v(std::in_place_type<unsigned>, 43u);500        V v2(std::in_place_type<TCopyAssign>, 42);501        v = v2;502        return {v.index(), std::get<1>(v).value};503      }504    } test;505    constexpr auto result = test();506    static_assert(result.index == 1, "");507    static_assert(result.value == 42, "");508  }509}510 511void test_assignment_throw() {512#ifndef TEST_HAS_NO_EXCEPTIONS513  using MET = MakeEmptyT;514  // same index515  {516    using V = std::variant<int, MET, std::string>;517    V v1(std::in_place_type<MET>);518    MET& mref = std::get<1>(v1);519    V v2(std::in_place_type<MET>);520    try {521      v1 = v2;522      assert(false);523    } catch (...) {524    }525    assert(v1.index() == 1);526    assert(&std::get<1>(v1) == &mref);527  }528 529  // difference indices530  {531    using V = std::variant<int, CopyThrows, std::string>;532    V v1(std::in_place_type<std::string>, "hello");533    V v2(std::in_place_type<CopyThrows>);534    try {535      v1 = v2;536      assert(false);537    } catch (...) { /* ... */538    }539    // Test that copy construction is used directly if move construction may throw,540    // resulting in a valueless variant if copy throws.541    assert(v1.valueless_by_exception());542  }543  {544    using V = std::variant<int, MoveThrows, std::string>;545    V v1(std::in_place_type<std::string>, "hello");546    V v2(std::in_place_type<MoveThrows>);547    assert(MoveThrows::alive == 1);548    // Test that copy construction is used directly if move construction may throw.549    v1 = v2;550    assert(v1.index() == 1);551    assert(v2.index() == 1);552    assert(MoveThrows::alive == 2);553  }554  {555    // Test that direct copy construction is preferred when it cannot throw.556    using V = std::variant<int, CopyCannotThrow, std::string>;557    V v1(std::in_place_type<std::string>, "hello");558    V v2(std::in_place_type<CopyCannotThrow>);559    assert(CopyCannotThrow::alive == 1);560    v1 = v2;561    assert(v1.index() == 1);562    assert(v2.index() == 1);563    assert(CopyCannotThrow::alive == 2);564  }565  {566    using V = std::variant<int, CopyThrows, std::string>;567    V v1(std::in_place_type<CopyThrows>);568    V v2(std::in_place_type<std::string>, "hello");569    V& vref = (v1 = v2);570    assert(&vref == &v1);571    assert(v1.index() == 2);572    assert(std::get<2>(v1) == "hello");573    assert(v2.index() == 2);574    assert(std::get<2>(v2) == "hello");575  }576  {577    using V = std::variant<int, MoveThrows, std::string>;578    V v1(std::in_place_type<MoveThrows>);579    V v2(std::in_place_type<std::string>, "hello");580    V& vref = (v1 = v2);581    assert(&vref == &v1);582    assert(v1.index() == 2);583    assert(std::get<2>(v1) == "hello");584    assert(v2.index() == 2);585    assert(std::get<2>(v2) == "hello");586  }587#endif // TEST_HAS_NO_EXCEPTIONS588}589 590template <std::size_t NewIdx, class T, class ValueType>591constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {592  using Variant = std::decay_t<T>;593  const Variant cp(std::forward<ValueType>(new_value));594  v = cp;595  assert(v.index() == NewIdx);596  assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));597}598 599constexpr void test_constexpr_copy_assignment_trivial() {600  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).601  using V = std::variant<long, void*, int>;602  static_assert(std::is_trivially_copyable<V>::value, "");603  static_assert(std::is_trivially_copy_assignable<V>::value, "");604  test_constexpr_assign_imp<0>(V(42l), 101l);605  test_constexpr_assign_imp<0>(V(nullptr), 101l);606  test_constexpr_assign_imp<1>(V(42l), nullptr);607  test_constexpr_assign_imp<2>(V(42l), 101);608}609 610struct NonTrivialCopyAssign {611  int i = 0;612  constexpr NonTrivialCopyAssign(int ii) : i(ii) {}613  constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {}614  constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) {615    i = o.i;616    return *this;617  }618  TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default;619  friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; }620};621 622constexpr void test_constexpr_copy_assignment_non_trivial() {623  // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).624  using V = std::variant<long, void*, NonTrivialCopyAssign>;625  static_assert(!std::is_trivially_copyable<V>::value, "");626  static_assert(!std::is_trivially_copy_assignable<V>::value, "");627  test_constexpr_assign_imp<0>(V(42l), 101l);628  test_constexpr_assign_imp<0>(V(nullptr), 101l);629  test_constexpr_assign_imp<1>(V(42l), nullptr);630  test_constexpr_assign_imp<2>(V(42l), NonTrivialCopyAssign(5));631  test_constexpr_assign_imp<2>(V(NonTrivialCopyAssign(3)), NonTrivialCopyAssign(5));632}633 634void non_constexpr_test() {635  test_copy_assignment_empty_empty();636  test_copy_assignment_non_empty_empty();637  test_copy_assignment_empty_non_empty();638  test_assignment_throw();639}640 641constexpr bool cxx17_constexpr_test() {642  test_copy_assignment_sfinae();643  test_copy_assignment_not_noexcept();644  test_constexpr_copy_assignment_trivial();645 646  return true;647}648 649TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {650  test_copy_assignment_same_index();651  test_copy_assignment_different_index();652  test_constexpr_copy_assignment_non_trivial();653 654  return true;655}656 657int main(int, char**) {658  non_constexpr_test();659  cxx17_constexpr_test();660  cxx20_constexpr_test();661 662  static_assert(cxx17_constexpr_test());663#if TEST_STD_VER >= 20664  static_assert(cxx20_constexpr_test());665#endif666  return 0;667}668