1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14
10
11 // <variant>
12
13 // template <class ...Types> class variant;
14
15 // constexpr variant(variant&&) noexcept(see below);
16
17 #include <cassert>
18 #include <string>
19 #include <type_traits>
20 #include <variant>
21
22 #include "test_macros.h"
23 #include "test_workarounds.h"
24
25 struct ThrowsMove {
ThrowsMoveThrowsMove26 ThrowsMove(ThrowsMove&&) noexcept(false) {}
27 };
28
29 struct NoCopy {
30 NoCopy(const NoCopy&) = delete;
31 };
32
33 struct MoveOnly {
34 int value;
MoveOnlyMoveOnly35 constexpr MoveOnly(int v) : value(v) {}
36 MoveOnly(const MoveOnly&) = delete;
37 MoveOnly(MoveOnly&&) = default;
38 };
39
40 struct MoveOnlyNT {
41 int value;
MoveOnlyNTMoveOnlyNT42 constexpr MoveOnlyNT(int v) : value(v) {}
43 MoveOnlyNT(const MoveOnlyNT&) = delete;
MoveOnlyNTMoveOnlyNT44 constexpr MoveOnlyNT(MoveOnlyNT&& other) : value(other.value) { other.value = -1; }
45 };
46
47 struct NTMove {
NTMoveNTMove48 constexpr NTMove(int v) : value(v) {}
49 NTMove(const NTMove&) = delete;
NTMoveNTMove50 NTMove(NTMove&& that) : value(that.value) { that.value = -1; }
51 int value;
52 };
53
54 static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
55 static_assert(std::is_move_constructible<NTMove>::value, "");
56
57 struct TMove {
TMoveTMove58 constexpr TMove(int v) : value(v) {}
59 TMove(const TMove&) = delete;
60 TMove(TMove&&) = default;
61 int value;
62 };
63
64 static_assert(std::is_trivially_move_constructible<TMove>::value, "");
65
66 struct TMoveNTCopy {
TMoveNTCopyTMoveNTCopy67 constexpr TMoveNTCopy(int v) : value(v) {}
TMoveNTCopyTMoveNTCopy68 TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {}
69 TMoveNTCopy(TMoveNTCopy&&) = default;
70 int value;
71 };
72
73 static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
74
75 #ifndef TEST_HAS_NO_EXCEPTIONS
76 struct MakeEmptyT {
77 static int alive;
MakeEmptyTMakeEmptyT78 MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT79 MakeEmptyT(const MakeEmptyT&) {
80 ++alive;
81 // Don't throw from the copy constructor since variant's assignment
82 // operator performs a copy before committing to the assignment.
83 }
MakeEmptyTMakeEmptyT84 MakeEmptyT(MakeEmptyT&&) { throw 42; }
operator =MakeEmptyT85 MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
operator =MakeEmptyT86 MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
~MakeEmptyTMakeEmptyT87 ~MakeEmptyT() { --alive; }
88 };
89
90 int MakeEmptyT::alive = 0;
91
92 template <class Variant>
makeEmpty(Variant & v)93 void makeEmpty(Variant& v) {
94 Variant v2(std::in_place_type<MakeEmptyT>);
95 try {
96 v = std::move(v2);
97 assert(false);
98 } catch (...) {
99 assert(v.valueless_by_exception());
100 }
101 }
102 #endif // TEST_HAS_NO_EXCEPTIONS
103
test_move_noexcept()104 constexpr void test_move_noexcept() {
105 {
106 using V = std::variant<int, long>;
107 static_assert(std::is_nothrow_move_constructible<V>::value, "");
108 }
109 {
110 using V = std::variant<int, MoveOnly>;
111 static_assert(std::is_nothrow_move_constructible<V>::value, "");
112 }
113 {
114 using V = std::variant<int, MoveOnlyNT>;
115 static_assert(!std::is_nothrow_move_constructible<V>::value, "");
116 }
117 {
118 using V = std::variant<int, ThrowsMove>;
119 static_assert(!std::is_nothrow_move_constructible<V>::value, "");
120 }
121 }
122
test_move_ctor_sfinae()123 constexpr void test_move_ctor_sfinae() {
124 {
125 using V = std::variant<int, long>;
126 static_assert(std::is_move_constructible<V>::value, "");
127 }
128 {
129 using V = std::variant<int, MoveOnly>;
130 static_assert(std::is_move_constructible<V>::value, "");
131 }
132 {
133 using V = std::variant<int, MoveOnlyNT>;
134 static_assert(std::is_move_constructible<V>::value, "");
135 }
136 {
137 using V = std::variant<int, NoCopy>;
138 static_assert(!std::is_move_constructible<V>::value, "");
139 }
140
141 // Make sure we properly propagate triviality (see P0602R4).
142 {
143 using V = std::variant<int, long>;
144 static_assert(std::is_trivially_move_constructible<V>::value, "");
145 }
146 {
147 using V = std::variant<int, NTMove>;
148 static_assert(!std::is_trivially_move_constructible<V>::value, "");
149 static_assert(std::is_move_constructible<V>::value, "");
150 }
151 {
152 using V = std::variant<int, TMove>;
153 static_assert(std::is_trivially_move_constructible<V>::value, "");
154 }
155 {
156 using V = std::variant<int, TMoveNTCopy>;
157 static_assert(std::is_trivially_move_constructible<V>::value, "");
158 }
159 }
160
161 template <typename T>
162 struct Result {
163 std::size_t index;
164 T value;
165 };
166
test_move_ctor_basic()167 TEST_CONSTEXPR_CXX20 void test_move_ctor_basic() {
168 {
169 std::variant<int> v(std::in_place_index<0>, 42);
170 std::variant<int> v2 = std::move(v);
171 assert(v2.index() == 0);
172 assert(std::get<0>(v2) == 42);
173 }
174 {
175 std::variant<int, long> v(std::in_place_index<1>, 42);
176 std::variant<int, long> v2 = std::move(v);
177 assert(v2.index() == 1);
178 assert(std::get<1>(v2) == 42);
179 }
180 {
181 std::variant<MoveOnly> v(std::in_place_index<0>, 42);
182 assert(v.index() == 0);
183 std::variant<MoveOnly> v2(std::move(v));
184 assert(v2.index() == 0);
185 assert(std::get<0>(v2).value == 42);
186 }
187 {
188 std::variant<int, MoveOnly> v(std::in_place_index<1>, 42);
189 assert(v.index() == 1);
190 std::variant<int, MoveOnly> v2(std::move(v));
191 assert(v2.index() == 1);
192 assert(std::get<1>(v2).value == 42);
193 }
194 {
195 std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42);
196 assert(v.index() == 0);
197 std::variant<MoveOnlyNT> v2(std::move(v));
198 assert(v2.index() == 0);
199 assert(std::get<0>(v).value == -1);
200 assert(std::get<0>(v2).value == 42);
201 }
202 {
203 std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42);
204 assert(v.index() == 1);
205 std::variant<int, MoveOnlyNT> v2(std::move(v));
206 assert(v2.index() == 1);
207 assert(std::get<1>(v).value == -1);
208 assert(std::get<1>(v2).value == 42);
209 }
210
211 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
212 {
213 struct {
214 constexpr Result<int> operator()() const {
215 std::variant<int> v(std::in_place_index<0>, 42);
216 std::variant<int> v2 = std::move(v);
217 return {v2.index(), std::get<0>(std::move(v2))};
218 }
219 } test;
220 constexpr auto result = test();
221 static_assert(result.index == 0, "");
222 static_assert(result.value == 42, "");
223 }
224 {
225 struct {
226 constexpr Result<long> operator()() const {
227 std::variant<int, long> v(std::in_place_index<1>, 42);
228 std::variant<int, long> v2 = std::move(v);
229 return {v2.index(), std::get<1>(std::move(v2))};
230 }
231 } test;
232 constexpr auto result = test();
233 static_assert(result.index == 1, "");
234 static_assert(result.value == 42, "");
235 }
236 {
237 struct {
238 constexpr Result<TMove> operator()() const {
239 std::variant<TMove> v(std::in_place_index<0>, 42);
240 std::variant<TMove> v2(std::move(v));
241 return {v2.index(), std::get<0>(std::move(v2))};
242 }
243 } test;
244 constexpr auto result = test();
245 static_assert(result.index == 0, "");
246 static_assert(result.value.value == 42, "");
247 }
248 {
249 struct {
250 constexpr Result<TMove> operator()() const {
251 std::variant<int, TMove> v(std::in_place_index<1>, 42);
252 std::variant<int, TMove> v2(std::move(v));
253 return {v2.index(), std::get<1>(std::move(v2))};
254 }
255 } test;
256 constexpr auto result = test();
257 static_assert(result.index == 1, "");
258 static_assert(result.value.value == 42, "");
259 }
260 {
261 struct {
262 constexpr Result<TMoveNTCopy> operator()() const {
263 std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42);
264 std::variant<TMoveNTCopy> v2(std::move(v));
265 return {v2.index(), std::get<0>(std::move(v2))};
266 }
267 } test;
268 constexpr auto result = test();
269 static_assert(result.index == 0, "");
270 static_assert(result.value.value == 42, "");
271 }
272 {
273 struct {
274 constexpr Result<TMoveNTCopy> operator()() const {
275 std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42);
276 std::variant<int, TMoveNTCopy> v2(std::move(v));
277 return {v2.index(), std::get<1>(std::move(v2))};
278 }
279 } test;
280 constexpr auto result = test();
281 static_assert(result.index == 1, "");
282 static_assert(result.value.value == 42, "");
283 }
284 }
285
test_move_ctor_valueless_by_exception()286 void test_move_ctor_valueless_by_exception() {
287 #ifndef TEST_HAS_NO_EXCEPTIONS
288 using V = std::variant<int, MakeEmptyT>;
289 V v1;
290 makeEmpty(v1);
291 V v(std::move(v1));
292 assert(v.valueless_by_exception());
293 #endif // TEST_HAS_NO_EXCEPTIONS
294 }
295
296 template <std::size_t Idx, class T>
test_constexpr_ctor_imp(const T & v)297 constexpr void test_constexpr_ctor_imp(const T& v) {
298 auto copy = v;
299 auto v2 = std::move(copy);
300 assert(v2.index() == v.index());
301 assert(v2.index() == Idx);
302 assert(std::get<Idx>(v2) == std::get<Idx>(v));
303 }
304
test_constexpr_move_ctor_trivial()305 constexpr void test_constexpr_move_ctor_trivial() {
306 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
307 using V = std::variant<long, void*, const int>;
308 #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
309 static_assert(std::is_trivially_destructible<V>::value, "");
310 static_assert(std::is_trivially_copy_constructible<V>::value, "");
311 static_assert(std::is_trivially_move_constructible<V>::value, "");
312 static_assert(!std::is_copy_assignable<V>::value, "");
313 static_assert(!std::is_move_assignable<V>::value, "");
314 #else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
315 static_assert(std::is_trivially_copyable<V>::value, "");
316 #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
317 static_assert(std::is_trivially_move_constructible<V>::value, "");
318 test_constexpr_ctor_imp<0>(V(42l));
319 test_constexpr_ctor_imp<1>(V(nullptr));
320 test_constexpr_ctor_imp<2>(V(101));
321 }
322
323 struct NonTrivialMoveCtor {
324 int i = 0;
NonTrivialMoveCtorNonTrivialMoveCtor325 constexpr NonTrivialMoveCtor(int ii) : i(ii) {}
326 constexpr NonTrivialMoveCtor(const NonTrivialMoveCtor& other) = default;
NonTrivialMoveCtorNonTrivialMoveCtor327 constexpr NonTrivialMoveCtor(NonTrivialMoveCtor&& other) : i(other.i) {}
328 TEST_CONSTEXPR_CXX20 ~NonTrivialMoveCtor() = default;
operator ==(const NonTrivialMoveCtor & x,const NonTrivialMoveCtor & y)329 friend constexpr bool operator==(const NonTrivialMoveCtor& x, const NonTrivialMoveCtor& y) { return x.i == y.i; }
330 };
331
test_constexpr_move_ctor_non_trivial()332 TEST_CONSTEXPR_CXX20 void test_constexpr_move_ctor_non_trivial() {
333 using V = std::variant<long, NonTrivialMoveCtor, void*>;
334 static_assert(!std::is_trivially_move_constructible<V>::value, "");
335 test_constexpr_ctor_imp<0>(V(42l));
336 test_constexpr_ctor_imp<1>(V(NonTrivialMoveCtor(5)));
337 test_constexpr_ctor_imp<2>(V(nullptr));
338 }
339
non_constexpr_test()340 void non_constexpr_test() { test_move_ctor_valueless_by_exception(); }
341
cxx17_constexpr_test()342 constexpr bool cxx17_constexpr_test() {
343 test_move_noexcept();
344 test_move_ctor_sfinae();
345 test_constexpr_move_ctor_trivial();
346
347 return true;
348 }
349
cxx20_constexpr_test()350 TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
351 test_move_ctor_basic();
352 test_constexpr_move_ctor_non_trivial();
353
354 return true;
355 }
356
main(int,char **)357 int main(int, char**) {
358 non_constexpr_test();
359 cxx17_constexpr_test();
360 cxx20_constexpr_test();
361
362 static_assert(cxx17_constexpr_test());
363 #if TEST_STD_VER >= 20
364 static_assert(cxx20_constexpr_test());
365 #endif
366
367 return 0;
368 }
369