1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++98, c++03, c++11, c++14
11 
12 // The following compilers don't generate constexpr special members correctly.
13 // XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8
14 // XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0
15 
16 // XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions
17 
18 
19 // <variant>
20 
21 // template <class ...Types> class variant;
22 
23 // variant& operator=(variant&&) noexcept(see below); // constexpr in C++20
24 
25 #include <cassert>
26 #include <string>
27 #include <type_traits>
28 #include <utility>
29 #include <variant>
30 
31 #include "test_macros.h"
32 #include "variant_test_helpers.h"
33 
34 struct NoCopy {
35   NoCopy(const NoCopy &) = delete;
36   NoCopy &operator=(const NoCopy &) = default;
37 };
38 
39 struct CopyOnly {
40   CopyOnly(const CopyOnly &) = default;
41   CopyOnly(CopyOnly &&) = delete;
42   CopyOnly &operator=(const CopyOnly &) = default;
43   CopyOnly &operator=(CopyOnly &&) = delete;
44 };
45 
46 struct MoveOnly {
47   MoveOnly(const MoveOnly &) = delete;
48   MoveOnly(MoveOnly &&) = default;
49   MoveOnly &operator=(const MoveOnly &) = delete;
50   MoveOnly &operator=(MoveOnly &&) = default;
51 };
52 
53 struct MoveOnlyNT {
54   MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT55   MoveOnlyNT(MoveOnlyNT &&) {}
56   MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
57   MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
58 };
59 
60 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow61   MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
62   MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
63   MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
64   MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
65 };
66 
67 struct MoveAssignOnly {
68   MoveAssignOnly(MoveAssignOnly &&) = delete;
69   MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
70 };
71 
72 struct MoveAssign {
73   static int move_construct;
74   static int move_assign;
resetMoveAssign75   static void reset() { move_construct = move_assign = 0; }
MoveAssignMoveAssign76   MoveAssign(int v) : value(v) {}
MoveAssignMoveAssign77   MoveAssign(MoveAssign &&o) : value(o.value) {
78     ++move_construct;
79     o.value = -1;
80   }
operator =MoveAssign81   MoveAssign &operator=(MoveAssign &&o) {
82     value = o.value;
83     ++move_assign;
84     o.value = -1;
85     return *this;
86   }
87   int value;
88 };
89 
90 int MoveAssign::move_construct = 0;
91 int MoveAssign::move_assign = 0;
92 
93 struct NTMoveAssign {
NTMoveAssignNTMoveAssign94   constexpr NTMoveAssign(int v) : value(v) {}
95   NTMoveAssign(const NTMoveAssign &) = default;
96   NTMoveAssign(NTMoveAssign &&) = default;
97   NTMoveAssign &operator=(const NTMoveAssign &that) = default;
operator =NTMoveAssign98   NTMoveAssign &operator=(NTMoveAssign &&that) {
99     value = that.value;
100     that.value = -1;
101     return *this;
102   };
103   int value;
104 };
105 
106 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
107 static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
108 
109 struct TMoveAssign {
TMoveAssignTMoveAssign110   constexpr TMoveAssign(int v) : value(v) {}
111   TMoveAssign(const TMoveAssign &) = delete;
112   TMoveAssign(TMoveAssign &&) = default;
113   TMoveAssign &operator=(const TMoveAssign &) = delete;
114   TMoveAssign &operator=(TMoveAssign &&) = default;
115   int value;
116 };
117 
118 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
119 
120 struct TMoveAssignNTCopyAssign {
TMoveAssignNTCopyAssignTMoveAssignNTCopyAssign121   constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
122   TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default;
123   TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default;
operator =TMoveAssignNTCopyAssign124   TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) {
125     value = that.value;
126     return *this;
127   }
128   TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default;
129   int value;
130 };
131 
132 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
133 
134 struct TrivialCopyNontrivialMove {
135   TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
TrivialCopyNontrivialMoveTrivialCopyNontrivialMove136   TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
137   TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
operator =TrivialCopyNontrivialMove138   TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept {
139     return *this;
140   }
141 };
142 
143 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
144 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
145 
146 
test_move_assignment_noexcept()147 void test_move_assignment_noexcept() {
148   {
149     using V = std::variant<int>;
150     static_assert(std::is_nothrow_move_assignable<V>::value, "");
151   }
152   {
153     using V = std::variant<MoveOnly>;
154     static_assert(std::is_nothrow_move_assignable<V>::value, "");
155   }
156   {
157     using V = std::variant<int, long>;
158     static_assert(std::is_nothrow_move_assignable<V>::value, "");
159   }
160   {
161     using V = std::variant<int, MoveOnly>;
162     static_assert(std::is_nothrow_move_assignable<V>::value, "");
163   }
164   {
165     using V = std::variant<MoveOnlyNT>;
166     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
167   }
168   {
169     using V = std::variant<MoveOnlyOddNothrow>;
170     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
171   }
172 }
173 
test_move_assignment_sfinae()174 void test_move_assignment_sfinae() {
175   {
176     using V = std::variant<int, long>;
177     static_assert(std::is_move_assignable<V>::value, "");
178   }
179   {
180     using V = std::variant<int, CopyOnly>;
181     static_assert(std::is_move_assignable<V>::value, "");
182   }
183   {
184     using V = std::variant<int, NoCopy>;
185     static_assert(!std::is_move_assignable<V>::value, "");
186   }
187   {
188     using V = std::variant<int, MoveOnly>;
189     static_assert(std::is_move_assignable<V>::value, "");
190   }
191   {
192     using V = std::variant<int, MoveOnlyNT>;
193     static_assert(std::is_move_assignable<V>::value, "");
194   }
195   {
196     // variant only provides move assignment when the types also provide
197     // a move constructor.
198     using V = std::variant<int, MoveAssignOnly>;
199     static_assert(!std::is_move_assignable<V>::value, "");
200   }
201 
202   // Make sure we properly propagate triviality (see P0602R4).
203 #if TEST_STD_VER > 17
204   {
205     using V = std::variant<int, long>;
206     static_assert(std::is_trivially_move_assignable<V>::value, "");
207   }
208   {
209     using V = std::variant<int, NTMoveAssign>;
210     static_assert(!std::is_trivially_move_assignable<V>::value, "");
211     static_assert(std::is_move_assignable<V>::value, "");
212   }
213   {
214     using V = std::variant<int, TMoveAssign>;
215     static_assert(std::is_trivially_move_assignable<V>::value, "");
216   }
217   {
218     using V = std::variant<int, TMoveAssignNTCopyAssign>;
219     static_assert(std::is_trivially_move_assignable<V>::value, "");
220   }
221   {
222     using V = std::variant<int, TrivialCopyNontrivialMove>;
223     static_assert(!std::is_trivially_move_assignable<V>::value, "");
224   }
225   {
226     using V = std::variant<int, CopyOnly>;
227     static_assert(std::is_trivially_move_assignable<V>::value, "");
228   }
229 #endif // > C++17
230 }
231 
test_move_assignment_empty_empty()232 void test_move_assignment_empty_empty() {
233 #ifndef TEST_HAS_NO_EXCEPTIONS
234   using MET = MakeEmptyT;
235   {
236     using V = std::variant<int, long, MET>;
237     V v1(std::in_place_index<0>);
238     makeEmpty(v1);
239     V v2(std::in_place_index<0>);
240     makeEmpty(v2);
241     V &vref = (v1 = std::move(v2));
242     assert(&vref == &v1);
243     assert(v1.valueless_by_exception());
244     assert(v1.index() == std::variant_npos);
245   }
246 #endif // TEST_HAS_NO_EXCEPTIONS
247 }
248 
test_move_assignment_non_empty_empty()249 void test_move_assignment_non_empty_empty() {
250 #ifndef TEST_HAS_NO_EXCEPTIONS
251   using MET = MakeEmptyT;
252   {
253     using V = std::variant<int, MET>;
254     V v1(std::in_place_index<0>, 42);
255     V v2(std::in_place_index<0>);
256     makeEmpty(v2);
257     V &vref = (v1 = std::move(v2));
258     assert(&vref == &v1);
259     assert(v1.valueless_by_exception());
260     assert(v1.index() == std::variant_npos);
261   }
262   {
263     using V = std::variant<int, MET, std::string>;
264     V v1(std::in_place_index<2>, "hello");
265     V v2(std::in_place_index<0>);
266     makeEmpty(v2);
267     V &vref = (v1 = std::move(v2));
268     assert(&vref == &v1);
269     assert(v1.valueless_by_exception());
270     assert(v1.index() == std::variant_npos);
271   }
272 #endif // TEST_HAS_NO_EXCEPTIONS
273 }
274 
test_move_assignment_empty_non_empty()275 void test_move_assignment_empty_non_empty() {
276 #ifndef TEST_HAS_NO_EXCEPTIONS
277   using MET = MakeEmptyT;
278   {
279     using V = std::variant<int, MET>;
280     V v1(std::in_place_index<0>);
281     makeEmpty(v1);
282     V v2(std::in_place_index<0>, 42);
283     V &vref = (v1 = std::move(v2));
284     assert(&vref == &v1);
285     assert(v1.index() == 0);
286     assert(std::get<0>(v1) == 42);
287   }
288   {
289     using V = std::variant<int, MET, std::string>;
290     V v1(std::in_place_index<0>);
291     makeEmpty(v1);
292     V v2(std::in_place_type<std::string>, "hello");
293     V &vref = (v1 = std::move(v2));
294     assert(&vref == &v1);
295     assert(v1.index() == 2);
296     assert(std::get<2>(v1) == "hello");
297   }
298 #endif // TEST_HAS_NO_EXCEPTIONS
299 }
300 
301 template <typename T> struct Result { size_t index; T value; };
302 
test_move_assignment_same_index()303 void test_move_assignment_same_index() {
304   {
305     using V = std::variant<int>;
306     V v1(43);
307     V v2(42);
308     V &vref = (v1 = std::move(v2));
309     assert(&vref == &v1);
310     assert(v1.index() == 0);
311     assert(std::get<0>(v1) == 42);
312   }
313   {
314     using V = std::variant<int, long, unsigned>;
315     V v1(43l);
316     V v2(42l);
317     V &vref = (v1 = std::move(v2));
318     assert(&vref == &v1);
319     assert(v1.index() == 1);
320     assert(std::get<1>(v1) == 42);
321   }
322   {
323     using V = std::variant<int, MoveAssign, unsigned>;
324     V v1(std::in_place_type<MoveAssign>, 43);
325     V v2(std::in_place_type<MoveAssign>, 42);
326     MoveAssign::reset();
327     V &vref = (v1 = std::move(v2));
328     assert(&vref == &v1);
329     assert(v1.index() == 1);
330     assert(std::get<1>(v1).value == 42);
331     assert(MoveAssign::move_construct == 0);
332     assert(MoveAssign::move_assign == 1);
333   }
334 #ifndef TEST_HAS_NO_EXCEPTIONS
335   using MET = MakeEmptyT;
336   {
337     using V = std::variant<int, MET, std::string>;
338     V v1(std::in_place_type<MET>);
339     MET &mref = std::get<1>(v1);
340     V v2(std::in_place_type<MET>);
341     try {
342       v1 = std::move(v2);
343       assert(false);
344     } catch (...) {
345     }
346     assert(v1.index() == 1);
347     assert(&std::get<1>(v1) == &mref);
348   }
349 #endif // TEST_HAS_NO_EXCEPTIONS
350 
351   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
352 #if TEST_STD_VER > 17
353   {
354     struct {
355       constexpr Result<int> operator()() const {
356         using V = std::variant<int>;
357         V v(43);
358         V v2(42);
359         v = std::move(v2);
360         return {v.index(), std::get<0>(v)};
361       }
362     } test;
363     constexpr auto result = test();
364     static_assert(result.index == 0, "");
365     static_assert(result.value == 42, "");
366   }
367   {
368     struct {
369       constexpr Result<long> operator()() const {
370         using V = std::variant<int, long, unsigned>;
371         V v(43l);
372         V v2(42l);
373         v = std::move(v2);
374         return {v.index(), std::get<1>(v)};
375       }
376     } test;
377     constexpr auto result = test();
378     static_assert(result.index == 1, "");
379     static_assert(result.value == 42l, "");
380   }
381   {
382     struct {
383       constexpr Result<int> operator()() const {
384         using V = std::variant<int, TMoveAssign, unsigned>;
385         V v(std::in_place_type<TMoveAssign>, 43);
386         V v2(std::in_place_type<TMoveAssign>, 42);
387         v = std::move(v2);
388         return {v.index(), std::get<1>(v).value};
389       }
390     } test;
391     constexpr auto result = test();
392     static_assert(result.index == 1, "");
393     static_assert(result.value == 42, "");
394   }
395 #endif // > C++17
396 }
397 
test_move_assignment_different_index()398 void test_move_assignment_different_index() {
399   {
400     using V = std::variant<int, long, unsigned>;
401     V v1(43);
402     V v2(42l);
403     V &vref = (v1 = std::move(v2));
404     assert(&vref == &v1);
405     assert(v1.index() == 1);
406     assert(std::get<1>(v1) == 42);
407   }
408   {
409     using V = std::variant<int, MoveAssign, unsigned>;
410     V v1(std::in_place_type<unsigned>, 43u);
411     V v2(std::in_place_type<MoveAssign>, 42);
412     MoveAssign::reset();
413     V &vref = (v1 = std::move(v2));
414     assert(&vref == &v1);
415     assert(v1.index() == 1);
416     assert(std::get<1>(v1).value == 42);
417     assert(MoveAssign::move_construct == 1);
418     assert(MoveAssign::move_assign == 0);
419   }
420 #ifndef TEST_HAS_NO_EXCEPTIONS
421   using MET = MakeEmptyT;
422   {
423     using V = std::variant<int, MET, std::string>;
424     V v1(std::in_place_type<int>);
425     V v2(std::in_place_type<MET>);
426     try {
427       v1 = std::move(v2);
428       assert(false);
429     } catch (...) {
430     }
431     assert(v1.valueless_by_exception());
432     assert(v1.index() == std::variant_npos);
433   }
434   {
435     using V = std::variant<int, MET, std::string>;
436     V v1(std::in_place_type<MET>);
437     V v2(std::in_place_type<std::string>, "hello");
438     V &vref = (v1 = std::move(v2));
439     assert(&vref == &v1);
440     assert(v1.index() == 2);
441     assert(std::get<2>(v1) == "hello");
442   }
443 #endif // TEST_HAS_NO_EXCEPTIONS
444 
445   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
446 #if TEST_STD_VER > 17
447   {
448     struct {
449       constexpr Result<long> operator()() const {
450         using V = std::variant<int, long, unsigned>;
451         V v(43);
452         V v2(42l);
453         v = std::move(v2);
454         return {v.index(), std::get<1>(v)};
455       }
456     } test;
457     constexpr auto result = test();
458     static_assert(result.index == 1, "");
459     static_assert(result.value == 42l, "");
460   }
461   {
462     struct {
463       constexpr Result<long> operator()() const {
464         using V = std::variant<int, TMoveAssign, unsigned>;
465         V v(std::in_place_type<unsigned>, 43u);
466         V v2(std::in_place_type<TMoveAssign>, 42);
467         v = std::move(v2);
468         return {v.index(), std::get<1>(v).value};
469       }
470     } test;
471     constexpr auto result = test();
472     static_assert(result.index == 1, "");
473     static_assert(result.value == 42, "");
474   }
475 #endif // > C++17
476 }
477 
478 template <size_t NewIdx, class ValueType>
test_constexpr_assign_imp(std::variant<long,void *,int> && v,ValueType && new_value)479 constexpr bool test_constexpr_assign_imp(
480     std::variant<long, void*, int>&& v, ValueType&& new_value)
481 {
482   std::variant<long, void*, int> v2(
483       std::forward<ValueType>(new_value));
484   const auto cp = v2;
485   v = std::move(v2);
486   return v.index() == NewIdx &&
487         std::get<NewIdx>(v) == std::get<NewIdx>(cp);
488 }
489 
test_constexpr_move_assignment()490 void test_constexpr_move_assignment() {
491   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
492 #if TEST_STD_VER > 17
493   using V = std::variant<long, void*, int>;
494   static_assert(std::is_trivially_copyable<V>::value, "");
495   static_assert(std::is_trivially_move_assignable<V>::value, "");
496   static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
497   static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
498   static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
499   static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
500 #endif // > C++17
501 }
502 
main(int,char **)503 int main(int, char**) {
504   test_move_assignment_empty_empty();
505   test_move_assignment_non_empty_empty();
506   test_move_assignment_empty_non_empty();
507   test_move_assignment_same_index();
508   test_move_assignment_different_index();
509   test_move_assignment_sfinae();
510   test_move_assignment_noexcept();
511   test_constexpr_move_assignment();
512 
513   return 0;
514 }
515