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