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-exception
6//
7//===----------------------------------------------------------------------===//
8
9// UNSUPPORTED: c++03, c++11, c++14
10
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 static int alive;
50 static int copy_construct;
51 static int copy_assign;
52 static int move_construct;
53 static int move_assign;
54 static void reset() {
55 copy_construct = copy_assign = move_construct = move_assign = alive = 0;
56 }
57 CopyAssign(int v) : value(v) { ++alive; }
58 CopyAssign(const CopyAssign &o) : value(o.value) {
59 ++alive;
60 ++copy_construct;
61 }
62 CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
63 o.value = -1;
64 ++alive;
65 ++move_construct;
66 }
67 CopyAssign &operator=(const CopyAssign &o) {
68 value = o.value;
69 ++copy_assign;
70 return *this;
71 }
72 CopyAssign &operator=(CopyAssign &&o) noexcept {
73 value = o.value;
74 o.value = -1;
75 ++move_assign;
76 return *this;
77 }
78 ~CopyAssign() { --alive; }
79 int value;
80};
81
82int CopyAssign::alive = 0;
83int CopyAssign::copy_construct = 0;
84int CopyAssign::copy_assign = 0;
85int CopyAssign::move_construct = 0;
86int CopyAssign::move_assign = 0;
87
88struct CopyMaybeThrows {
89 CopyMaybeThrows(const CopyMaybeThrows &);
90 CopyMaybeThrows &operator=(const CopyMaybeThrows &);
91};
92struct CopyDoesThrow {
93 CopyDoesThrow(const CopyDoesThrow &) noexcept(false);
94 CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false);
95};
96
97
98struct NTCopyAssign {
99 constexpr NTCopyAssign(int v) : value(v) {}
100 NTCopyAssign(const NTCopyAssign &) = default;
101 NTCopyAssign(NTCopyAssign &&) = default;
102 NTCopyAssign &operator=(const NTCopyAssign &that) {
103 value = that.value;
104 return *this;
105 };
106 NTCopyAssign &operator=(NTCopyAssign &&) = delete;
107 int value;
108};
109
110static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");
111static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
112
113struct TCopyAssign {
114 constexpr TCopyAssign(int v) : value(v) {}
115 TCopyAssign(const TCopyAssign &) = default;
116 TCopyAssign(TCopyAssign &&) = default;
117 TCopyAssign &operator=(const TCopyAssign &) = default;
118 TCopyAssign &operator=(TCopyAssign &&) = delete;
119 int value;
120};
121
122static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
123
124struct TCopyAssignNTMoveAssign {
125 constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
126 TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default;
127 TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default;
128 TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default;
129 TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) {
130 value = that.value;
131 that.value = -1;
132 return *this;
133 }
134 int value;
135};
136
137static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
138
139#ifndef TEST_HAS_NO_EXCEPTIONS
140struct CopyThrows {
141 CopyThrows() = default;
142 CopyThrows(const CopyThrows &) { throw 42; }
143 CopyThrows &operator=(const CopyThrows &) { throw 42; }
144};
145
146struct CopyCannotThrow {
147 static int alive;
148 CopyCannotThrow() { ++alive; }
149 CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
150 CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
151 CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
152 CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
153};
154
155int CopyCannotThrow::alive = 0;
156
157struct MoveThrows {
158 static int alive;
159 MoveThrows() { ++alive; }
160 MoveThrows(const MoveThrows &) { ++alive; }
161 MoveThrows(MoveThrows &&) { throw 42; }
162 MoveThrows &operator=(const MoveThrows &) { return *this; }
163 MoveThrows &operator=(MoveThrows &&) { throw 42; }
164 ~MoveThrows() { --alive; }
165};
166
167int MoveThrows::alive = 0;
168
169struct MakeEmptyT {
170 static int alive;
171 MakeEmptyT() { ++alive; }
172 MakeEmptyT(const MakeEmptyT &) {
173 ++alive;
174 // Don't throw from the copy constructor since variant's assignment
175 // operator performs a copy before committing to the assignment.
176 }
177 MakeEmptyT(MakeEmptyT &&) { throw 42; }
178 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
179 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
180 ~MakeEmptyT() { --alive; }
181};
182
183int MakeEmptyT::alive = 0;
184
185template <class Variant> void makeEmpty(Variant &v) {
186 Variant v2(std::in_place_type<MakeEmptyT>);
187 try {
188 v = std::move(v2);
189 assert(false);
190 } catch (...) {
191 assert(v.valueless_by_exception());
192 }
193}
194#endif // TEST_HAS_NO_EXCEPTIONS
195
196void test_copy_assignment_not_noexcept() {
197 {
198 using V = std::variant<CopyMaybeThrows>;
199 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
200 }
201 {
202 using V = std::variant<int, CopyDoesThrow>;
203 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
204 }
205}
206
207void test_copy_assignment_sfinae() {
208 {
209 using V = std::variant<int, long>;
210 static_assert(std::is_copy_assignable<V>::value, "");
211 }
212 {
213 using V = std::variant<int, CopyOnly>;
214 static_assert(std::is_copy_assignable<V>::value, "");
215 }
216 {
217 using V = std::variant<int, NoCopy>;
218 static_assert(!std::is_copy_assignable<V>::value, "");
219 }
220 {
221 using V = std::variant<int, MoveOnly>;
222 static_assert(!std::is_copy_assignable<V>::value, "");
223 }
224 {
225 using V = std::variant<int, MoveOnlyNT>;
226 static_assert(!std::is_copy_assignable<V>::value, "");
227 }
228
229 // Make sure we properly propagate triviality (see P0602R4).
230 {
231 using V = std::variant<int, long>;
232 static_assert(std::is_trivially_copy_assignable<V>::value, "");
233 }
234 {
235 using V = std::variant<int, NTCopyAssign>;
236 static_assert(!std::is_trivially_copy_assignable<V>::value, "");
237 static_assert(std::is_copy_assignable<V>::value, "");
238 }
239 {
240 using V = std::variant<int, TCopyAssign>;
241 static_assert(std::is_trivially_copy_assignable<V>::value, "");
242 }
243 {
244 using V = std::variant<int, TCopyAssignNTMoveAssign>;
245 static_assert(std::is_trivially_copy_assignable<V>::value, "");
246 }
247 {
248 using V = std::variant<int, CopyOnly>;
249 static_assert(std::is_trivially_copy_assignable<V>::value, "");
250 }
251}
252
253void test_copy_assignment_empty_empty() {
254#ifndef TEST_HAS_NO_EXCEPTIONS
255 using MET = MakeEmptyT;
256 {
257 using V = std::variant<int, long, MET>;
258 V v1(std::in_place_index<0>);
259 makeEmpty(v&: v1);
260 V v2(std::in_place_index<0>);
261 makeEmpty(v&: v2);
262 V &vref = (v1 = v2);
263 assert(&vref == &v1);
264 assert(v1.valueless_by_exception());
265 assert(v1.index() == std::variant_npos);
266 }
267#endif // TEST_HAS_NO_EXCEPTIONS
268}
269
270void test_copy_assignment_non_empty_empty() {
271#ifndef TEST_HAS_NO_EXCEPTIONS
272 using MET = MakeEmptyT;
273 {
274 using V = std::variant<int, MET>;
275 V v1(std::in_place_index<0>, 42);
276 V v2(std::in_place_index<0>);
277 makeEmpty(v&: v2);
278 V &vref = (v1 = v2);
279 assert(&vref == &v1);
280 assert(v1.valueless_by_exception());
281 assert(v1.index() == std::variant_npos);
282 }
283 {
284 using V = std::variant<int, MET, std::string>;
285 V v1(std::in_place_index<2>, "hello");
286 V v2(std::in_place_index<0>);
287 makeEmpty(v&: v2);
288 V &vref = (v1 = v2);
289 assert(&vref == &v1);
290 assert(v1.valueless_by_exception());
291 assert(v1.index() == std::variant_npos);
292 }
293#endif // TEST_HAS_NO_EXCEPTIONS
294}
295
296void test_copy_assignment_empty_non_empty() {
297#ifndef TEST_HAS_NO_EXCEPTIONS
298 using MET = MakeEmptyT;
299 {
300 using V = std::variant<int, MET>;
301 V v1(std::in_place_index<0>);
302 makeEmpty(v&: v1);
303 V v2(std::in_place_index<0>, 42);
304 V &vref = (v1 = v2);
305 assert(&vref == &v1);
306 assert(v1.index() == 0);
307 assert(std::get<0>(v1) == 42);
308 }
309 {
310 using V = std::variant<int, MET, std::string>;
311 V v1(std::in_place_index<0>);
312 makeEmpty(v&: v1);
313 V v2(std::in_place_type<std::string>, "hello");
314 V &vref = (v1 = v2);
315 assert(&vref == &v1);
316 assert(v1.index() == 2);
317 assert(std::get<2>(v1) == "hello");
318 }
319#endif // TEST_HAS_NO_EXCEPTIONS
320}
321
322template <typename T> struct Result { std::size_t index; T value; };
323
324void test_copy_assignment_same_index() {
325 {
326 using V = std::variant<int>;
327 V v1(43);
328 V v2(42);
329 V &vref = (v1 = v2);
330 assert(&vref == &v1);
331 assert(v1.index() == 0);
332 assert(std::get<0>(v1) == 42);
333 }
334 {
335 using V = std::variant<int, long, unsigned>;
336 V v1(43l);
337 V v2(42l);
338 V &vref = (v1 = v2);
339 assert(&vref == &v1);
340 assert(v1.index() == 1);
341 assert(std::get<1>(v1) == 42);
342 }
343 {
344 using V = std::variant<int, CopyAssign, unsigned>;
345 V v1(std::in_place_type<CopyAssign>, 43);
346 V v2(std::in_place_type<CopyAssign>, 42);
347 CopyAssign::reset();
348 V &vref = (v1 = v2);
349 assert(&vref == &v1);
350 assert(v1.index() == 1);
351 assert(std::get<1>(v1).value == 42);
352 assert(CopyAssign::copy_construct == 0);
353 assert(CopyAssign::move_construct == 0);
354 assert(CopyAssign::copy_assign == 1);
355 }
356#ifndef TEST_HAS_NO_EXCEPTIONS
357 using MET = MakeEmptyT;
358 {
359 using V = std::variant<int, MET, std::string>;
360 V v1(std::in_place_type<MET>);
361 MET &mref = std::get<1>(v&: v1);
362 V v2(std::in_place_type<MET>);
363 try {
364 v1 = v2;
365 assert(false);
366 } catch (...) {
367 }
368 assert(v1.index() == 1);
369 assert(&std::get<1>(v1) == &mref);
370 }
371#endif // TEST_HAS_NO_EXCEPTIONS
372
373 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
374 {
375 struct {
376 constexpr Result<int> operator()() const {
377 using V = std::variant<int>;
378 V v(43);
379 V v2(42);
380 v = v2;
381 return {.index: v.index(), .value: std::get<0>(v&: v)};
382 }
383 } test;
384 constexpr auto result = test();
385 static_assert(result.index == 0, "");
386 static_assert(result.value == 42, "");
387 }
388 {
389 struct {
390 constexpr Result<long> operator()() const {
391 using V = std::variant<int, long, unsigned>;
392 V v(43l);
393 V v2(42l);
394 v = v2;
395 return {.index: v.index(), .value: std::get<1>(v&: v)};
396 }
397 } test;
398 constexpr auto result = test();
399 static_assert(result.index == 1, "");
400 static_assert(result.value == 42l, "");
401 }
402 {
403 struct {
404 constexpr Result<int> operator()() const {
405 using V = std::variant<int, TCopyAssign, unsigned>;
406 V v(std::in_place_type<TCopyAssign>, 43);
407 V v2(std::in_place_type<TCopyAssign>, 42);
408 v = v2;
409 return {.index: v.index(), .value: std::get<1>(v&: v).value};
410 }
411 } test;
412 constexpr auto result = test();
413 static_assert(result.index == 1, "");
414 static_assert(result.value == 42, "");
415 }
416 {
417 struct {
418 constexpr Result<int> operator()() const {
419 using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;
420 V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);
421 V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);
422 v = v2;
423 return {.index: v.index(), .value: std::get<1>(v&: v).value};
424 }
425 } test;
426 constexpr auto result = test();
427 static_assert(result.index == 1, "");
428 static_assert(result.value == 42, "");
429 }
430}
431
432void test_copy_assignment_different_index() {
433 {
434 using V = std::variant<int, long, unsigned>;
435 V v1(43);
436 V v2(42l);
437 V &vref = (v1 = v2);
438 assert(&vref == &v1);
439 assert(v1.index() == 1);
440 assert(std::get<1>(v1) == 42);
441 }
442 {
443 using V = std::variant<int, CopyAssign, unsigned>;
444 CopyAssign::reset();
445 V v1(std::in_place_type<unsigned>, 43u);
446 V v2(std::in_place_type<CopyAssign>, 42);
447 assert(CopyAssign::copy_construct == 0);
448 assert(CopyAssign::move_construct == 0);
449 assert(CopyAssign::alive == 1);
450 V &vref = (v1 = v2);
451 assert(&vref == &v1);
452 assert(v1.index() == 1);
453 assert(std::get<1>(v1).value == 42);
454 assert(CopyAssign::alive == 2);
455 assert(CopyAssign::copy_construct == 1);
456 assert(CopyAssign::move_construct == 1);
457 assert(CopyAssign::copy_assign == 0);
458 }
459#ifndef TEST_HAS_NO_EXCEPTIONS
460 {
461 using V = std::variant<int, CopyThrows, std::string>;
462 V v1(std::in_place_type<std::string>, "hello");
463 V v2(std::in_place_type<CopyThrows>);
464 try {
465 v1 = v2;
466 assert(false);
467 } catch (...) { /* ... */
468 }
469 // Test that copy construction is used directly if move construction may throw,
470 // resulting in a valueless variant if copy throws.
471 assert(v1.valueless_by_exception());
472 }
473 {
474 using V = std::variant<int, MoveThrows, std::string>;
475 V v1(std::in_place_type<std::string>, "hello");
476 V v2(std::in_place_type<MoveThrows>);
477 assert(MoveThrows::alive == 1);
478 // Test that copy construction is used directly if move construction may throw.
479 v1 = v2;
480 assert(v1.index() == 1);
481 assert(v2.index() == 1);
482 assert(MoveThrows::alive == 2);
483 }
484 {
485 // Test that direct copy construction is preferred when it cannot throw.
486 using V = std::variant<int, CopyCannotThrow, std::string>;
487 V v1(std::in_place_type<std::string>, "hello");
488 V v2(std::in_place_type<CopyCannotThrow>);
489 assert(CopyCannotThrow::alive == 1);
490 v1 = v2;
491 assert(v1.index() == 1);
492 assert(v2.index() == 1);
493 assert(CopyCannotThrow::alive == 2);
494 }
495 {
496 using V = std::variant<int, CopyThrows, std::string>;
497 V v1(std::in_place_type<CopyThrows>);
498 V v2(std::in_place_type<std::string>, "hello");
499 V &vref = (v1 = v2);
500 assert(&vref == &v1);
501 assert(v1.index() == 2);
502 assert(std::get<2>(v1) == "hello");
503 assert(v2.index() == 2);
504 assert(std::get<2>(v2) == "hello");
505 }
506 {
507 using V = std::variant<int, MoveThrows, std::string>;
508 V v1(std::in_place_type<MoveThrows>);
509 V v2(std::in_place_type<std::string>, "hello");
510 V &vref = (v1 = v2);
511 assert(&vref == &v1);
512 assert(v1.index() == 2);
513 assert(std::get<2>(v1) == "hello");
514 assert(v2.index() == 2);
515 assert(std::get<2>(v2) == "hello");
516 }
517#endif // TEST_HAS_NO_EXCEPTIONS
518
519 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
520 {
521 struct {
522 constexpr Result<long> operator()() const {
523 using V = std::variant<int, long, unsigned>;
524 V v(43);
525 V v2(42l);
526 v = v2;
527 return {.index: v.index(), .value: std::get<1>(v&: v)};
528 }
529 } test;
530 constexpr auto result = test();
531 static_assert(result.index == 1, "");
532 static_assert(result.value == 42l, "");
533 }
534 {
535 struct {
536 constexpr Result<int> operator()() const {
537 using V = std::variant<int, TCopyAssign, unsigned>;
538 V v(std::in_place_type<unsigned>, 43u);
539 V v2(std::in_place_type<TCopyAssign>, 42);
540 v = v2;
541 return {.index: v.index(), .value: std::get<1>(v&: v).value};
542 }
543 } test;
544 constexpr auto result = test();
545 static_assert(result.index == 1, "");
546 static_assert(result.value == 42, "");
547 }
548}
549
550template <std::size_t NewIdx, class ValueType>
551constexpr bool test_constexpr_assign_imp(
552 std::variant<long, void*, int>&& v, ValueType&& new_value)
553{
554 const std::variant<long, void*, int> cp(
555 std::forward<ValueType>(new_value));
556 v = cp;
557 return v.index() == NewIdx &&
558 std::get<NewIdx>(v) == std::get<NewIdx>(cp);
559}
560
561void test_constexpr_copy_assignment() {
562 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
563 using V = std::variant<long, void*, int>;
564 static_assert(std::is_trivially_copyable<V>::value, "");
565 static_assert(std::is_trivially_copy_assignable<V>::value, "");
566 static_assert(test_constexpr_assign_imp<0>(v: V(42l), new_value: 101l), "");
567 static_assert(test_constexpr_assign_imp<0>(v: V(nullptr), new_value: 101l), "");
568 static_assert(test_constexpr_assign_imp<1>(v: V(42l), new_value: nullptr), "");
569 static_assert(test_constexpr_assign_imp<2>(v: V(42l), new_value: 101), "");
570}
571
572int main(int, char**) {
573 test_copy_assignment_empty_empty();
574 test_copy_assignment_non_empty_empty();
575 test_copy_assignment_empty_non_empty();
576 test_copy_assignment_same_index();
577 test_copy_assignment_different_index();
578 test_copy_assignment_sfinae();
579 test_copy_assignment_not_noexcept();
580 test_constexpr_copy_assignment();
581
582 return 0;
583}
584

source code of libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp