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