xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp (revision 52271a5c11f6abde1fa1221db304212b5eb8ec7c)
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 
24 struct NoCopy {
25   NoCopy(const NoCopy&)            = delete;
26   NoCopy& operator=(const NoCopy&) = default;
27 };
28 
29 struct CopyOnly {
30   CopyOnly(const CopyOnly&)            = default;
31   CopyOnly(CopyOnly&&)                 = delete;
32   CopyOnly& operator=(const CopyOnly&) = default;
33   CopyOnly& operator=(CopyOnly&&)      = delete;
34 };
35 
36 struct MoveOnly {
37   MoveOnly(const MoveOnly&)            = delete;
38   MoveOnly(MoveOnly&&)                 = default;
39   MoveOnly& operator=(const MoveOnly&) = default;
40 };
41 
42 struct MoveOnlyNT {
43   MoveOnlyNT(const MoveOnlyNT&) = delete;
MoveOnlyNTMoveOnlyNT44   MoveOnlyNT(MoveOnlyNT&&) {}
45   MoveOnlyNT& operator=(const MoveOnlyNT&) = default;
46 };
47 
48 struct CopyAssign {
CopyAssignCopyAssign49   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   }
CopyAssignCopyAssign58   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   }
CopyAssignCopyAssign68   constexpr CopyAssign(CopyAssign&& o) noexcept
69       : 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   }
operator =CopyAssign79   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   }
operator =CopyAssign89   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   }
~CopyAssignCopyAssign100   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 
109 struct CopyMaybeThrows {
110   CopyMaybeThrows(const CopyMaybeThrows&);
111   CopyMaybeThrows& operator=(const CopyMaybeThrows&);
112 };
113 struct CopyDoesThrow {
114   CopyDoesThrow(const CopyDoesThrow&) noexcept(false);
115   CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false);
116 };
117 
118 struct NTCopyAssign {
NTCopyAssignNTCopyAssign119   constexpr NTCopyAssign(int v) : value(v) {}
120   NTCopyAssign(const NTCopyAssign&) = default;
121   NTCopyAssign(NTCopyAssign&&)      = default;
operator =NTCopyAssign122   NTCopyAssign& operator=(const NTCopyAssign& that) {
123     value = that.value;
124     return *this;
125   };
126   NTCopyAssign& operator=(NTCopyAssign&&) = delete;
127   int value;
128 };
129 
130 static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");
131 static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
132 
133 struct TCopyAssign {
TCopyAssignTCopyAssign134   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 
142 static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
143 
144 struct TCopyAssignNTMoveAssign {
TCopyAssignNTMoveAssignTCopyAssignNTMoveAssign145   constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
146   TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&)            = default;
147   TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&)                 = default;
148   TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default;
operator =TCopyAssignNTMoveAssign149   TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) {
150     value      = that.value;
151     that.value = -1;
152     return *this;
153   }
154   int value;
155 };
156 
157 static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
158 
159 #ifndef TEST_HAS_NO_EXCEPTIONS
160 struct CopyThrows {
161   CopyThrows() = default;
CopyThrowsCopyThrows162   CopyThrows(const CopyThrows&) { throw 42; }
operator =CopyThrows163   CopyThrows& operator=(const CopyThrows&) { throw 42; }
164 };
165 
166 struct CopyCannotThrow {
167   static int alive;
CopyCannotThrowCopyCannotThrow168   CopyCannotThrow() { ++alive; }
CopyCannotThrowCopyCannotThrow169   CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; }
CopyCannotThrowCopyCannotThrow170   CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); }
171   CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default;
operator =CopyCannotThrow172   CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept {
173     assert(false);
174     return *this;
175   }
176 };
177 
178 int CopyCannotThrow::alive = 0;
179 
180 struct MoveThrows {
181   static int alive;
MoveThrowsMoveThrows182   MoveThrows() { ++alive; }
MoveThrowsMoveThrows183   MoveThrows(const MoveThrows&) { ++alive; }
MoveThrowsMoveThrows184   MoveThrows(MoveThrows&&) { throw 42; }
operator =MoveThrows185   MoveThrows& operator=(const MoveThrows&) { return *this; }
operator =MoveThrows186   MoveThrows& operator=(MoveThrows&&) { throw 42; }
~MoveThrowsMoveThrows187   ~MoveThrows() { --alive; }
188 };
189 
190 int MoveThrows::alive = 0;
191 
192 struct MakeEmptyT {
193   static int alive;
MakeEmptyTMakeEmptyT194   MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT195   MakeEmptyT(const MakeEmptyT&) {
196     ++alive;
197     // Don't throw from the copy constructor since variant's assignment
198     // operator performs a copy before committing to the assignment.
199   }
MakeEmptyTMakeEmptyT200   MakeEmptyT(MakeEmptyT&&) { throw 42; }
operator =MakeEmptyT201   MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
operator =MakeEmptyT202   MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
~MakeEmptyTMakeEmptyT203   ~MakeEmptyT() { --alive; }
204 };
205 
206 int MakeEmptyT::alive = 0;
207 
208 template <class Variant>
makeEmpty(Variant & v)209 void 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_EXCEPTIONS
219 
test_copy_assignment_not_noexcept()220 constexpr 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 
test_copy_assignment_sfinae()231 constexpr 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 
test_copy_assignment_empty_empty()277 void test_copy_assignment_empty_empty() {
278 #ifndef TEST_HAS_NO_EXCEPTIONS
279   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_EXCEPTIONS
292 }
293 
test_copy_assignment_non_empty_empty()294 void test_copy_assignment_non_empty_empty() {
295 #ifndef TEST_HAS_NO_EXCEPTIONS
296   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_EXCEPTIONS
318 }
319 
test_copy_assignment_empty_non_empty()320 void test_copy_assignment_empty_non_empty() {
321 #ifndef TEST_HAS_NO_EXCEPTIONS
322   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_EXCEPTIONS
344 }
345 
346 template <typename T>
347 struct Result {
348   std::size_t index;
349   T value;
350 };
351 
test_copy_assignment_same_index()352 TEST_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 
test_copy_assignment_different_index()448 TEST_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 
test_assignment_throw()511 void test_assignment_throw() {
512 #ifndef TEST_HAS_NO_EXCEPTIONS
513   using MET = MakeEmptyT;
514   // same index
515   {
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 indices
530   {
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_EXCEPTIONS
588 }
589 
590 template <std::size_t NewIdx, class T, class ValueType>
test_constexpr_assign_imp(T && v,ValueType && new_value)591 constexpr 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 
test_constexpr_copy_assignment_trivial()599 constexpr 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 
610 struct NonTrivialCopyAssign {
611   int i = 0;
NonTrivialCopyAssignNonTrivialCopyAssign612   constexpr NonTrivialCopyAssign(int ii) : i(ii) {}
NonTrivialCopyAssignNonTrivialCopyAssign613   constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {}
operator =NonTrivialCopyAssign614   constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) {
615     i = o.i;
616     return *this;
617   }
618   TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default;
operator ==(const NonTrivialCopyAssign & x,const NonTrivialCopyAssign & y)619   friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; }
620 };
621 
test_constexpr_copy_assignment_non_trivial()622 constexpr 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 
non_constexpr_test()634 void 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 
cxx17_constexpr_test()641 constexpr 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 
cxx20_constexpr_test()649 TEST_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 
main(int,char **)657 int 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 >= 20
664   static_assert(cxx20_constexpr_test());
665 #endif
666   return 0;
667 }
668