• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
MoveOnlyNTMoveOnlyNT51   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;
resetCopyAssign61   static void reset() {
62     copy_construct = copy_assign = move_construct = move_assign = alive = 0;
63   }
CopyAssignCopyAssign64   CopyAssign(int v) : value(v) { ++alive; }
CopyAssignCopyAssign65   CopyAssign(const CopyAssign &o) : value(o.value) {
66     ++alive;
67     ++copy_construct;
68   }
CopyAssignCopyAssign69   CopyAssign(CopyAssign &&o) : value(o.value) {
70     o.value = -1;
71     ++alive;
72     ++move_construct;
73   }
operator =CopyAssign74   CopyAssign &operator=(const CopyAssign &o) {
75     value = o.value;
76     ++copy_assign;
77     return *this;
78   }
operator =CopyAssign79   CopyAssign &operator=(CopyAssign &&o) {
80     value = o.value;
81     o.value = -1;
82     ++move_assign;
83     return *this;
84   }
~CopyAssignCopyAssign85   ~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;
CopyThrowsCopyThrows107   CopyThrows(const CopyThrows &) { throw 42; }
operator =CopyThrows108   CopyThrows &operator=(const CopyThrows &) { throw 42; }
109 };
110 
111 struct MoveThrows {
112   static int alive;
MoveThrowsMoveThrows113   MoveThrows() { ++alive; }
MoveThrowsMoveThrows114   MoveThrows(const MoveThrows &) { ++alive; }
MoveThrowsMoveThrows115   MoveThrows(MoveThrows &&) { throw 42; }
operator =MoveThrows116   MoveThrows &operator=(const MoveThrows &) { return *this; }
operator =MoveThrows117   MoveThrows &operator=(MoveThrows &&) { throw 42; }
~MoveThrowsMoveThrows118   ~MoveThrows() { --alive; }
119 };
120 
121 int MoveThrows::alive = 0;
122 
123 struct MakeEmptyT {
124   static int alive;
MakeEmptyTMakeEmptyT125   MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT126   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   }
MakeEmptyTMakeEmptyT131   MakeEmptyT(MakeEmptyT &&) { throw 42; }
operator =MakeEmptyT132   MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
operator =MakeEmptyT133   MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
~MakeEmptyTMakeEmptyT134   ~MakeEmptyT() { --alive; }
135 };
136 
137 int MakeEmptyT::alive = 0;
138 
makeEmpty(Variant & v)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 
test_copy_assignment_not_noexcept()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 
test_copy_assignment_sfinae()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 
test_copy_assignment_empty_empty()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 
test_copy_assignment_non_empty_empty()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 
test_copy_assignment_empty_non_empty()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 
test_copy_assignment_same_index()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 
test_copy_assignment_different_index()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 
main()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