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