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