Home | History | Annotate | Download | only in variant.assign
      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 // <variant>
     14 
     15 // template <class ...Types> class variant;
     16 
     17 // variant& operator=(variant const&);
     18 
     19 #include <cassert>
     20 #include <string>
     21 #include <type_traits>
     22 #include <variant>
     23 
     24 #include "test_macros.h"
     25 
     26 struct NoCopy {
     27   NoCopy(const NoCopy &) = delete;
     28   NoCopy &operator=(const NoCopy &) = default;
     29 };
     30 
     31 struct NothrowCopy {
     32   NothrowCopy(const NothrowCopy &) noexcept = default;
     33   NothrowCopy &operator=(const NothrowCopy &) noexcept = default;
     34 };
     35 
     36 struct CopyOnly {
     37   CopyOnly(const CopyOnly &) = default;
     38   CopyOnly(CopyOnly &&) = delete;
     39   CopyOnly &operator=(const CopyOnly &) = default;
     40   CopyOnly &operator=(CopyOnly &&) = delete;
     41 };
     42 
     43 struct MoveOnly {
     44   MoveOnly(const MoveOnly &) = delete;
     45   MoveOnly(MoveOnly &&) = default;
     46   MoveOnly &operator=(const MoveOnly &) = default;
     47 };
     48 
     49 struct MoveOnlyNT {
     50   MoveOnlyNT(const MoveOnlyNT &) = delete;
     51   MoveOnlyNT(MoveOnlyNT &&) {}
     52   MoveOnlyNT &operator=(const MoveOnlyNT &) = default;
     53 };
     54 
     55 struct CopyAssign {
     56   static int alive;
     57   static int copy_construct;
     58   static int copy_assign;
     59   static int move_construct;
     60   static int move_assign;
     61   static void reset() {
     62     copy_construct = copy_assign = move_construct = move_assign = alive = 0;
     63   }
     64   CopyAssign(int v) : value(v) { ++alive; }
     65   CopyAssign(const CopyAssign &o) : value(o.value) {
     66     ++alive;
     67     ++copy_construct;
     68   }
     69   CopyAssign(CopyAssign &&o) : value(o.value) {
     70     o.value = -1;
     71     ++alive;
     72     ++move_construct;
     73   }
     74   CopyAssign &operator=(const CopyAssign &o) {
     75     value = o.value;
     76     ++copy_assign;
     77     return *this;
     78   }
     79   CopyAssign &operator=(CopyAssign &&o) {
     80     value = o.value;
     81     o.value = -1;
     82     ++move_assign;
     83     return *this;
     84   }
     85   ~CopyAssign() { --alive; }
     86   int value;
     87 };
     88 
     89 int CopyAssign::alive = 0;
     90 int CopyAssign::copy_construct = 0;
     91 int CopyAssign::copy_assign = 0;
     92 int CopyAssign::move_construct = 0;
     93 int CopyAssign::move_assign = 0;
     94 
     95 struct CopyMaybeThrows {
     96   CopyMaybeThrows(const CopyMaybeThrows &);
     97   CopyMaybeThrows &operator=(const CopyMaybeThrows &);
     98 };
     99 struct CopyDoesThrow {
    100   CopyDoesThrow(const CopyDoesThrow &) noexcept(false);
    101   CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false);
    102 };
    103 
    104 #ifndef TEST_HAS_NO_EXCEPTIONS
    105 struct CopyThrows {
    106   CopyThrows() = default;
    107   CopyThrows(const CopyThrows &) { throw 42; }
    108   CopyThrows &operator=(const CopyThrows &) { throw 42; }
    109 };
    110 
    111 struct MoveThrows {
    112   static int alive;
    113   MoveThrows() { ++alive; }
    114   MoveThrows(const MoveThrows &) { ++alive; }
    115   MoveThrows(MoveThrows &&) { throw 42; }
    116   MoveThrows &operator=(const MoveThrows &) { return *this; }
    117   MoveThrows &operator=(MoveThrows &&) { throw 42; }
    118   ~MoveThrows() { --alive; }
    119 };
    120 
    121 int MoveThrows::alive = 0;
    122 
    123 struct MakeEmptyT {
    124   static int alive;
    125   MakeEmptyT() { ++alive; }
    126   MakeEmptyT(const MakeEmptyT &) {
    127     ++alive;
    128     // Don't throw from the copy constructor since variant's assignment
    129     // operator performs a copy before committing to the assignment.
    130   }
    131   MakeEmptyT(MakeEmptyT &&) { throw 42; }
    132   MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
    133   MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
    134   ~MakeEmptyT() { --alive; }
    135 };
    136 
    137 int MakeEmptyT::alive = 0;
    138 
    139 template <class Variant> void makeEmpty(Variant &v) {
    140   Variant v2(std::in_place_type<MakeEmptyT>);
    141   try {
    142     v = v2;
    143     assert(false);
    144   } catch (...) {
    145     assert(v.valueless_by_exception());
    146   }
    147 }
    148 #endif // TEST_HAS_NO_EXCEPTIONS
    149 
    150 void test_copy_assignment_not_noexcept() {
    151   {
    152     using V = std::variant<CopyMaybeThrows>;
    153     static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
    154   }
    155   {
    156     using V = std::variant<int, CopyDoesThrow>;
    157     static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
    158   }
    159 }
    160 
    161 void test_copy_assignment_sfinae() {
    162   {
    163     using V = std::variant<int, long>;
    164     static_assert(std::is_copy_assignable<V>::value, "");
    165   }
    166   {
    167     // variant only provides copy assignment when both the copy and move
    168     // constructors are well formed
    169     using V = std::variant<int, CopyOnly>;
    170     static_assert(!std::is_copy_assignable<V>::value, "");
    171   }
    172   {
    173     using V = std::variant<int, NoCopy>;
    174     static_assert(!std::is_copy_assignable<V>::value, "");
    175   }
    176   {
    177     using V = std::variant<int, MoveOnly>;
    178     static_assert(!std::is_copy_assignable<V>::value, "");
    179   }
    180   {
    181     using V = std::variant<int, MoveOnlyNT>;
    182     static_assert(!std::is_copy_assignable<V>::value, "");
    183   }
    184 }
    185 
    186 void test_copy_assignment_empty_empty() {
    187 #ifndef TEST_HAS_NO_EXCEPTIONS
    188   using MET = MakeEmptyT;
    189   {
    190     using V = std::variant<int, long, MET>;
    191     V v1(std::in_place_index<0>);
    192     makeEmpty(v1);
    193     V v2(std::in_place_index<0>);
    194     makeEmpty(v2);
    195     V &vref = (v1 = v2);
    196     assert(&vref == &v1);
    197     assert(v1.valueless_by_exception());
    198     assert(v1.index() == std::variant_npos);
    199   }
    200 #endif
    201 }
    202 
    203 void test_copy_assignment_non_empty_empty() {
    204 #ifndef TEST_HAS_NO_EXCEPTIONS
    205   using MET = MakeEmptyT;
    206   {
    207     using V = std::variant<int, MET>;
    208     V v1(std::in_place_index<0>, 42);
    209     V v2(std::in_place_index<0>);
    210     makeEmpty(v2);
    211     V &vref = (v1 = v2);
    212     assert(&vref == &v1);
    213     assert(v1.valueless_by_exception());
    214     assert(v1.index() == std::variant_npos);
    215   }
    216   {
    217     using V = std::variant<int, MET, std::string>;
    218     V v1(std::in_place_index<2>, "hello");
    219     V v2(std::in_place_index<0>);
    220     makeEmpty(v2);
    221     V &vref = (v1 = v2);
    222     assert(&vref == &v1);
    223     assert(v1.valueless_by_exception());
    224     assert(v1.index() == std::variant_npos);
    225   }
    226 #endif
    227 }
    228 
    229 void test_copy_assignment_empty_non_empty() {
    230 #ifndef TEST_HAS_NO_EXCEPTIONS
    231   using MET = MakeEmptyT;
    232   {
    233     using V = std::variant<int, MET>;
    234     V v1(std::in_place_index<0>);
    235     makeEmpty(v1);
    236     V v2(std::in_place_index<0>, 42);
    237     V &vref = (v1 = v2);
    238     assert(&vref == &v1);
    239     assert(v1.index() == 0);
    240     assert(std::get<0>(v1) == 42);
    241   }
    242   {
    243     using V = std::variant<int, MET, std::string>;
    244     V v1(std::in_place_index<0>);
    245     makeEmpty(v1);
    246     V v2(std::in_place_type<std::string>, "hello");
    247     V &vref = (v1 = v2);
    248     assert(&vref == &v1);
    249     assert(v1.index() == 2);
    250     assert(std::get<2>(v1) == "hello");
    251   }
    252 #endif
    253 }
    254 
    255 void test_copy_assignment_same_index() {
    256   {
    257     using V = std::variant<int>;
    258     V v1(43);
    259     V v2(42);
    260     V &vref = (v1 = v2);
    261     assert(&vref == &v1);
    262     assert(v1.index() == 0);
    263     assert(std::get<0>(v1) == 42);
    264   }
    265   {
    266     using V = std::variant<int, long, unsigned>;
    267     V v1(43l);
    268     V v2(42l);
    269     V &vref = (v1 = v2);
    270     assert(&vref == &v1);
    271     assert(v1.index() == 1);
    272     assert(std::get<1>(v1) == 42);
    273   }
    274   {
    275     using V = std::variant<int, CopyAssign, unsigned>;
    276     V v1(std::in_place_type<CopyAssign>, 43);
    277     V v2(std::in_place_type<CopyAssign>, 42);
    278     CopyAssign::reset();
    279     V &vref = (v1 = v2);
    280     assert(&vref == &v1);
    281     assert(v1.index() == 1);
    282     assert(std::get<1>(v1).value == 42);
    283     assert(CopyAssign::copy_construct == 0);
    284     assert(CopyAssign::move_construct == 0);
    285     assert(CopyAssign::copy_assign == 1);
    286   }
    287 #ifndef TEST_HAS_NO_EXCEPTIONS
    288   using MET = MakeEmptyT;
    289   {
    290     using V = std::variant<int, MET, std::string>;
    291     V v1(std::in_place_type<MET>);
    292     MET &mref = std::get<1>(v1);
    293     V v2(std::in_place_type<MET>);
    294     try {
    295       v1 = v2;
    296       assert(false);
    297     } catch (...) {
    298     }
    299     assert(v1.index() == 1);
    300     assert(&std::get<1>(v1) == &mref);
    301   }
    302 #endif
    303 }
    304 
    305 void test_copy_assignment_different_index() {
    306   {
    307     using V = std::variant<int, long, unsigned>;
    308     V v1(43);
    309     V v2(42l);
    310     V &vref = (v1 = v2);
    311     assert(&vref == &v1);
    312     assert(v1.index() == 1);
    313     assert(std::get<1>(v1) == 42);
    314   }
    315   {
    316     using V = std::variant<int, CopyAssign, unsigned>;
    317     CopyAssign::reset();
    318     V v1(std::in_place_type<unsigned>, 43);
    319     V v2(std::in_place_type<CopyAssign>, 42);
    320     assert(CopyAssign::copy_construct == 0);
    321     assert(CopyAssign::move_construct == 0);
    322     assert(CopyAssign::alive == 1);
    323     V &vref = (v1 = v2);
    324     assert(&vref == &v1);
    325     assert(v1.index() == 1);
    326     assert(std::get<1>(v1).value == 42);
    327     assert(CopyAssign::alive == 2);
    328     assert(CopyAssign::copy_construct == 1);
    329     assert(CopyAssign::move_construct == 1);
    330     assert(CopyAssign::copy_assign == 0);
    331   }
    332 #ifndef TEST_HAS_NO_EXCEPTIONS
    333   {
    334     // Test that if copy construction throws then original value is
    335     // unchanged.
    336     using V = std::variant<int, CopyThrows, std::string>;
    337     V v1(std::in_place_type<std::string>, "hello");
    338     V v2(std::in_place_type<CopyThrows>);
    339     try {
    340       v1 = v2;
    341       assert(false);
    342     } catch (...) { /* ... */
    343     }
    344     assert(v1.index() == 2);
    345     assert(std::get<2>(v1) == "hello");
    346   }
    347   {
    348     // Test that if move construction throws then the variant is left
    349     // valueless by exception.
    350     using V = std::variant<int, MoveThrows, std::string>;
    351     V v1(std::in_place_type<std::string>, "hello");
    352     V v2(std::in_place_type<MoveThrows>);
    353     assert(MoveThrows::alive == 1);
    354     try {
    355       v1 = v2;
    356       assert(false);
    357     } catch (...) { /* ... */
    358     }
    359     assert(v1.valueless_by_exception());
    360     assert(v2.index() == 1);
    361     assert(MoveThrows::alive == 1);
    362   }
    363   {
    364     using V = std::variant<int, CopyThrows, std::string>;
    365     V v1(std::in_place_type<CopyThrows>);
    366     V v2(std::in_place_type<std::string>, "hello");
    367     V &vref = (v1 = v2);
    368     assert(&vref == &v1);
    369     assert(v1.index() == 2);
    370     assert(std::get<2>(v1) == "hello");
    371     assert(v2.index() == 2);
    372     assert(std::get<2>(v2) == "hello");
    373   }
    374   {
    375     using V = std::variant<int, MoveThrows, std::string>;
    376     V v1(std::in_place_type<MoveThrows>);
    377     V v2(std::in_place_type<std::string>, "hello");
    378     V &vref = (v1 = v2);
    379     assert(&vref == &v1);
    380     assert(v1.index() == 2);
    381     assert(std::get<2>(v1) == "hello");
    382     assert(v2.index() == 2);
    383     assert(std::get<2>(v2) == "hello");
    384   }
    385 #endif
    386 }
    387 
    388 
    389 int main() {
    390   test_copy_assignment_empty_empty();
    391   test_copy_assignment_non_empty_empty();
    392   test_copy_assignment_empty_non_empty();
    393   test_copy_assignment_same_index();
    394   test_copy_assignment_different_index();
    395   test_copy_assignment_sfinae();
    396   test_copy_assignment_not_noexcept();
    397 }
    398