xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.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&&) noexcept(see below);
16 
17 #include <cassert>
18 #include <string>
19 #include <type_traits>
20 #include <utility>
21 #include <variant>
22 
23 #include "test_macros.h"
24 #include "variant_test_helpers.h"
25 
26 struct NoCopy {
27   NoCopy(const NoCopy&)            = delete;
28   NoCopy& operator=(const NoCopy&) = default;
29 };
30 
31 struct CopyOnly {
32   CopyOnly(const CopyOnly&)            = default;
33   CopyOnly(CopyOnly&&)                 = delete;
34   CopyOnly& operator=(const CopyOnly&) = default;
35   CopyOnly& operator=(CopyOnly&&)      = delete;
36 };
37 
38 struct MoveOnly {
39   MoveOnly(const MoveOnly&)            = delete;
40   MoveOnly(MoveOnly&&)                 = default;
41   MoveOnly& operator=(const MoveOnly&) = delete;
42   MoveOnly& operator=(MoveOnly&&)      = default;
43 };
44 
45 struct MoveOnlyNT {
46   MoveOnlyNT(const MoveOnlyNT&) = delete;
MoveOnlyNTMoveOnlyNT47   MoveOnlyNT(MoveOnlyNT&&) {}
48   MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;
49   MoveOnlyNT& operator=(MoveOnlyNT&&)      = default;
50 };
51 
52 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow53   MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}
54   MoveOnlyOddNothrow(const MoveOnlyOddNothrow&)                = delete;
55   MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;
56   MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&)     = delete;
57 };
58 
59 struct MoveAssignOnly {
60   MoveAssignOnly(MoveAssignOnly&&)            = delete;
61   MoveAssignOnly& operator=(MoveAssignOnly&&) = default;
62 };
63 
64 struct MoveAssign {
MoveAssignMoveAssign65   constexpr MoveAssign(int v, int* move_ctor, int* move_assi)
66       : value(v), move_construct(move_ctor), move_assign(move_assi) {}
MoveAssignMoveAssign67   constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) {
68     ++*move_construct;
69     o.value = -1;
70   }
operator =MoveAssign71   constexpr MoveAssign& operator=(MoveAssign&& o) {
72     value          = o.value;
73     move_construct = o.move_construct;
74     move_assign    = o.move_assign;
75     ++*move_assign;
76     o.value = -1;
77     return *this;
78   }
79   int value;
80   int* move_construct;
81   int* move_assign;
82 };
83 
84 struct NTMoveAssign {
NTMoveAssignNTMoveAssign85   constexpr NTMoveAssign(int v) : value(v) {}
86   NTMoveAssign(const NTMoveAssign&)                 = default;
87   NTMoveAssign(NTMoveAssign&&)                      = default;
88   NTMoveAssign& operator=(const NTMoveAssign& that) = default;
operator =NTMoveAssign89   NTMoveAssign& operator=(NTMoveAssign&& that) {
90     value      = that.value;
91     that.value = -1;
92     return *this;
93   };
94   int value;
95 };
96 
97 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
98 static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
99 
100 struct TMoveAssign {
TMoveAssignTMoveAssign101   constexpr TMoveAssign(int v) : value(v) {}
102   TMoveAssign(const TMoveAssign&)            = delete;
103   TMoveAssign(TMoveAssign&&)                 = default;
104   TMoveAssign& operator=(const TMoveAssign&) = delete;
105   TMoveAssign& operator=(TMoveAssign&&)      = default;
106   int value;
107 };
108 
109 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
110 
111 struct TMoveAssignNTCopyAssign {
TMoveAssignNTCopyAssignTMoveAssignNTCopyAssign112   constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
113   TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;
114   TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&)      = default;
operator =TMoveAssignNTCopyAssign115   TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {
116     value = that.value;
117     return *this;
118   }
119   TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;
120   int value;
121 };
122 
123 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
124 
125 struct TrivialCopyNontrivialMove {
126   TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
TrivialCopyNontrivialMoveTrivialCopyNontrivialMove127   TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
128   TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
operator =TrivialCopyNontrivialMove129   TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }
130 };
131 
132 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
133 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
134 
test_move_assignment_noexcept()135 constexpr void test_move_assignment_noexcept() {
136   {
137     using V = std::variant<int>;
138     static_assert(std::is_nothrow_move_assignable<V>::value, "");
139   }
140   {
141     using V = std::variant<MoveOnly>;
142     static_assert(std::is_nothrow_move_assignable<V>::value, "");
143   }
144   {
145     using V = std::variant<int, long>;
146     static_assert(std::is_nothrow_move_assignable<V>::value, "");
147   }
148   {
149     using V = std::variant<int, MoveOnly>;
150     static_assert(std::is_nothrow_move_assignable<V>::value, "");
151   }
152   {
153     using V = std::variant<MoveOnlyNT>;
154     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
155   }
156   {
157     using V = std::variant<MoveOnlyOddNothrow>;
158     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
159   }
160 }
161 
test_move_assignment_sfinae()162 constexpr void test_move_assignment_sfinae() {
163   {
164     using V = std::variant<int, long>;
165     static_assert(std::is_move_assignable<V>::value, "");
166   }
167   {
168     using V = std::variant<int, CopyOnly>;
169     static_assert(std::is_move_assignable<V>::value, "");
170   }
171   {
172     using V = std::variant<int, NoCopy>;
173     static_assert(!std::is_move_assignable<V>::value, "");
174   }
175   {
176     using V = std::variant<int, MoveOnly>;
177     static_assert(std::is_move_assignable<V>::value, "");
178   }
179   {
180     using V = std::variant<int, MoveOnlyNT>;
181     static_assert(std::is_move_assignable<V>::value, "");
182   }
183   {
184     // variant only provides move assignment when the types also provide
185     // a move constructor.
186     using V = std::variant<int, MoveAssignOnly>;
187     static_assert(!std::is_move_assignable<V>::value, "");
188   }
189 
190   // Make sure we properly propagate triviality (see P0602R4).
191   {
192     using V = std::variant<int, long>;
193     static_assert(std::is_trivially_move_assignable<V>::value, "");
194   }
195   {
196     using V = std::variant<int, NTMoveAssign>;
197     static_assert(!std::is_trivially_move_assignable<V>::value, "");
198     static_assert(std::is_move_assignable<V>::value, "");
199   }
200   {
201     using V = std::variant<int, TMoveAssign>;
202     static_assert(std::is_trivially_move_assignable<V>::value, "");
203   }
204   {
205     using V = std::variant<int, TMoveAssignNTCopyAssign>;
206     static_assert(std::is_trivially_move_assignable<V>::value, "");
207   }
208   {
209     using V = std::variant<int, TrivialCopyNontrivialMove>;
210     static_assert(!std::is_trivially_move_assignable<V>::value, "");
211   }
212   {
213     using V = std::variant<int, CopyOnly>;
214     static_assert(std::is_trivially_move_assignable<V>::value, "");
215   }
216 }
217 
test_move_assignment_empty_empty()218 void test_move_assignment_empty_empty() {
219 #ifndef TEST_HAS_NO_EXCEPTIONS
220   using MET = MakeEmptyT;
221   {
222     using V = std::variant<int, long, MET>;
223     V v1(std::in_place_index<0>);
224     makeEmpty(v1);
225     V v2(std::in_place_index<0>);
226     makeEmpty(v2);
227     V& vref = (v1 = std::move(v2));
228     assert(&vref == &v1);
229     assert(v1.valueless_by_exception());
230     assert(v1.index() == std::variant_npos);
231   }
232 #endif // TEST_HAS_NO_EXCEPTIONS
233 }
234 
test_move_assignment_non_empty_empty()235 void test_move_assignment_non_empty_empty() {
236 #ifndef TEST_HAS_NO_EXCEPTIONS
237   using MET = MakeEmptyT;
238   {
239     using V = std::variant<int, MET>;
240     V v1(std::in_place_index<0>, 42);
241     V v2(std::in_place_index<0>);
242     makeEmpty(v2);
243     V& vref = (v1 = std::move(v2));
244     assert(&vref == &v1);
245     assert(v1.valueless_by_exception());
246     assert(v1.index() == std::variant_npos);
247   }
248   {
249     using V = std::variant<int, MET, std::string>;
250     V v1(std::in_place_index<2>, "hello");
251     V v2(std::in_place_index<0>);
252     makeEmpty(v2);
253     V& vref = (v1 = std::move(v2));
254     assert(&vref == &v1);
255     assert(v1.valueless_by_exception());
256     assert(v1.index() == std::variant_npos);
257   }
258 #endif // TEST_HAS_NO_EXCEPTIONS
259 }
260 
test_move_assignment_empty_non_empty()261 void test_move_assignment_empty_non_empty() {
262 #ifndef TEST_HAS_NO_EXCEPTIONS
263   using MET = MakeEmptyT;
264   {
265     using V = std::variant<int, MET>;
266     V v1(std::in_place_index<0>);
267     makeEmpty(v1);
268     V v2(std::in_place_index<0>, 42);
269     V& vref = (v1 = std::move(v2));
270     assert(&vref == &v1);
271     assert(v1.index() == 0);
272     assert(std::get<0>(v1) == 42);
273   }
274   {
275     using V = std::variant<int, MET, std::string>;
276     V v1(std::in_place_index<0>);
277     makeEmpty(v1);
278     V v2(std::in_place_type<std::string>, "hello");
279     V& vref = (v1 = std::move(v2));
280     assert(&vref == &v1);
281     assert(v1.index() == 2);
282     assert(std::get<2>(v1) == "hello");
283   }
284 #endif // TEST_HAS_NO_EXCEPTIONS
285 }
286 
287 template <typename T>
288 struct Result {
289   std::size_t index;
290   T value;
291 };
292 
test_move_assignment_same_index()293 TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() {
294   {
295     using V = std::variant<int>;
296     V v1(43);
297     V v2(42);
298     V& vref = (v1 = std::move(v2));
299     assert(&vref == &v1);
300     assert(v1.index() == 0);
301     assert(std::get<0>(v1) == 42);
302   }
303   {
304     using V = std::variant<int, long, unsigned>;
305     V v1(43l);
306     V v2(42l);
307     V& vref = (v1 = std::move(v2));
308     assert(&vref == &v1);
309     assert(v1.index() == 1);
310     assert(std::get<1>(v1) == 42);
311   }
312   {
313     using V            = std::variant<int, MoveAssign, unsigned>;
314     int move_construct = 0;
315     int move_assign    = 0;
316     V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);
317     V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
318     V& vref = (v1 = std::move(v2));
319     assert(&vref == &v1);
320     assert(v1.index() == 1);
321     assert(std::get<1>(v1).value == 42);
322     assert(move_construct == 0);
323     assert(move_assign == 1);
324   }
325 
326   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
327   {
328     struct {
329       constexpr Result<int> operator()() const {
330         using V = std::variant<int>;
331         V v(43);
332         V v2(42);
333         v = std::move(v2);
334         return {v.index(), std::get<0>(v)};
335       }
336     } test;
337     constexpr auto result = test();
338     static_assert(result.index == 0, "");
339     static_assert(result.value == 42, "");
340   }
341   {
342     struct {
343       constexpr Result<long> operator()() const {
344         using V = std::variant<int, long, unsigned>;
345         V v(43l);
346         V v2(42l);
347         v = std::move(v2);
348         return {v.index(), std::get<1>(v)};
349       }
350     } test;
351     constexpr auto result = test();
352     static_assert(result.index == 1, "");
353     static_assert(result.value == 42l, "");
354   }
355   {
356     struct {
357       constexpr Result<int> operator()() const {
358         using V = std::variant<int, TMoveAssign, unsigned>;
359         V v(std::in_place_type<TMoveAssign>, 43);
360         V v2(std::in_place_type<TMoveAssign>, 42);
361         v = std::move(v2);
362         return {v.index(), std::get<1>(v).value};
363       }
364     } test;
365     constexpr auto result = test();
366     static_assert(result.index == 1, "");
367     static_assert(result.value == 42, "");
368   }
369 }
370 
test_move_assignment_different_index()371 TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() {
372   {
373     using V = std::variant<int, long, unsigned>;
374     V v1(43);
375     V v2(42l);
376     V& vref = (v1 = std::move(v2));
377     assert(&vref == &v1);
378     assert(v1.index() == 1);
379     assert(std::get<1>(v1) == 42);
380   }
381   {
382     using V            = std::variant<int, MoveAssign, unsigned>;
383     int move_construct = 0;
384     int move_assign    = 0;
385     V v1(std::in_place_type<unsigned>, 43u);
386     V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
387     V& vref = (v1 = std::move(v2));
388     assert(&vref == &v1);
389     assert(v1.index() == 1);
390     assert(std::get<1>(v1).value == 42);
391     assert(move_construct == 1);
392     assert(move_assign == 0);
393   }
394 
395   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
396   {
397     struct {
398       constexpr Result<long> operator()() const {
399         using V = std::variant<int, long, unsigned>;
400         V v(43);
401         V v2(42l);
402         v = std::move(v2);
403         return {v.index(), std::get<1>(v)};
404       }
405     } test;
406     constexpr auto result = test();
407     static_assert(result.index == 1, "");
408     static_assert(result.value == 42l, "");
409   }
410   {
411     struct {
412       constexpr Result<long> operator()() const {
413         using V = std::variant<int, TMoveAssign, unsigned>;
414         V v(std::in_place_type<unsigned>, 43u);
415         V v2(std::in_place_type<TMoveAssign>, 42);
416         v = std::move(v2);
417         return {v.index(), std::get<1>(v).value};
418       }
419     } test;
420     constexpr auto result = test();
421     static_assert(result.index == 1, "");
422     static_assert(result.value == 42, "");
423   }
424 }
425 
test_assignment_throw()426 void test_assignment_throw() {
427 #ifndef TEST_HAS_NO_EXCEPTIONS
428   using MET = MakeEmptyT;
429   // same index
430   {
431     using V = std::variant<int, MET, std::string>;
432     V v1(std::in_place_type<MET>);
433     MET& mref = std::get<1>(v1);
434     V v2(std::in_place_type<MET>);
435     try {
436       v1 = std::move(v2);
437       assert(false);
438     } catch (...) {
439     }
440     assert(v1.index() == 1);
441     assert(&std::get<1>(v1) == &mref);
442   }
443 
444   // different indices
445   {
446     using V = std::variant<int, MET, std::string>;
447     V v1(std::in_place_type<int>);
448     V v2(std::in_place_type<MET>);
449     try {
450       v1 = std::move(v2);
451       assert(false);
452     } catch (...) {
453     }
454     assert(v1.valueless_by_exception());
455     assert(v1.index() == std::variant_npos);
456   }
457   {
458     using V = std::variant<int, MET, std::string>;
459     V v1(std::in_place_type<MET>);
460     V v2(std::in_place_type<std::string>, "hello");
461     V& vref = (v1 = std::move(v2));
462     assert(&vref == &v1);
463     assert(v1.index() == 2);
464     assert(std::get<2>(v1) == "hello");
465   }
466 #endif // TEST_HAS_NO_EXCEPTIONS
467 }
468 
469 template <std::size_t NewIdx, class T, class ValueType>
test_constexpr_assign_imp(T && v,ValueType && new_value)470 constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
471   using Variant = std::decay_t<T>;
472   Variant v2(std::forward<ValueType>(new_value));
473   const auto cp = v2;
474   v             = std::move(v2);
475   assert(v.index() == NewIdx);
476   assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));
477 }
478 
test_constexpr_move_assignment_trivial()479 constexpr void test_constexpr_move_assignment_trivial() {
480   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
481   using V = std::variant<long, void*, int>;
482   static_assert(std::is_trivially_copyable<V>::value, "");
483   static_assert(std::is_trivially_move_assignable<V>::value, "");
484   test_constexpr_assign_imp<0>(V(42l), 101l);
485   test_constexpr_assign_imp<0>(V(nullptr), 101l);
486   test_constexpr_assign_imp<1>(V(42l), nullptr);
487   test_constexpr_assign_imp<2>(V(42l), 101);
488 }
489 
490 struct NonTrivialMoveAssign {
491   int i = 0;
NonTrivialMoveAssignNonTrivialMoveAssign492   constexpr NonTrivialMoveAssign(int ii) : i(ii) {}
493   constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;
NonTrivialMoveAssignNonTrivialMoveAssign494   constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}
495   constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;
operator =NonTrivialMoveAssign496   constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {
497     i = o.i;
498     return *this;
499   }
500   TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;
operator ==(const NonTrivialMoveAssign & x,const NonTrivialMoveAssign & y)501   friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }
502 };
503 
test_constexpr_move_assignment_non_trivial()504 TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() {
505   using V = std::variant<long, void*, NonTrivialMoveAssign>;
506   static_assert(!std::is_trivially_copyable<V>::value);
507   static_assert(!std::is_trivially_move_assignable<V>::value);
508   test_constexpr_assign_imp<0>(V(42l), 101l);
509   test_constexpr_assign_imp<0>(V(nullptr), 101l);
510   test_constexpr_assign_imp<1>(V(42l), nullptr);
511   test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5));
512   test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5));
513 }
514 
non_constexpr_test()515 void non_constexpr_test() {
516   test_move_assignment_empty_empty();
517   test_move_assignment_non_empty_empty();
518   test_move_assignment_empty_non_empty();
519   test_assignment_throw();
520 }
521 
cxx17_constexpr_test()522 constexpr bool cxx17_constexpr_test() {
523   test_move_assignment_sfinae();
524   test_move_assignment_noexcept();
525   test_constexpr_move_assignment_trivial();
526 
527   return true;
528 }
529 
cxx20_constexpr_test()530 TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
531   test_move_assignment_same_index();
532   test_move_assignment_different_index();
533   test_constexpr_move_assignment_non_trivial();
534 
535   return true;
536 }
537 
main(int,char **)538 int main(int, char**) {
539   non_constexpr_test();
540   cxx17_constexpr_test();
541   cxx20_constexpr_test();
542 
543   static_assert(cxx17_constexpr_test());
544 #if TEST_STD_VER >= 20
545   static_assert(cxx20_constexpr_test());
546 #endif
547   return 0;
548 }
549