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