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 // Throwing bad_variant_access is supported starting in macosx10.13
12 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions
13
14 // <variant>
15
16 // template <class ...Types> class variant;
17
18 // variant(variant&&) noexcept(see below); // constexpr in C++20
19
20 #include <cassert>
21 #include <string>
22 #include <type_traits>
23 #include <variant>
24
25 #include "test_macros.h"
26 #include "test_workarounds.h"
27
28 struct ThrowsMove {
ThrowsMoveThrowsMove29 ThrowsMove(ThrowsMove &&) noexcept(false) {}
30 };
31
32 struct NoCopy {
33 NoCopy(const NoCopy &) = delete;
34 };
35
36 struct MoveOnly {
37 int value;
MoveOnlyMoveOnly38 MoveOnly(int v) : value(v) {}
39 MoveOnly(const MoveOnly &) = delete;
40 MoveOnly(MoveOnly &&) = default;
41 };
42
43 struct MoveOnlyNT {
44 int value;
MoveOnlyNTMoveOnlyNT45 MoveOnlyNT(int v) : value(v) {}
46 MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT47 MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; }
48 };
49
50 struct NTMove {
NTMoveNTMove51 constexpr NTMove(int v) : value(v) {}
52 NTMove(const NTMove &) = delete;
NTMoveNTMove53 NTMove(NTMove &&that) : value(that.value) { that.value = -1; }
54 int value;
55 };
56
57 static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
58 static_assert(std::is_move_constructible<NTMove>::value, "");
59
60 struct TMove {
TMoveTMove61 constexpr TMove(int v) : value(v) {}
62 TMove(const TMove &) = delete;
63 TMove(TMove &&) = default;
64 int value;
65 };
66
67 static_assert(std::is_trivially_move_constructible<TMove>::value, "");
68
69 struct TMoveNTCopy {
TMoveNTCopyTMoveNTCopy70 constexpr TMoveNTCopy(int v) : value(v) {}
TMoveNTCopyTMoveNTCopy71 TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {}
72 TMoveNTCopy(TMoveNTCopy&&) = default;
73 int value;
74 };
75
76 static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
77
78 #ifndef TEST_HAS_NO_EXCEPTIONS
79 struct MakeEmptyT {
80 static int alive;
MakeEmptyTMakeEmptyT81 MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT82 MakeEmptyT(const MakeEmptyT &) {
83 ++alive;
84 // Don't throw from the copy constructor since variant's assignment
85 // operator performs a copy before committing to the assignment.
86 }
MakeEmptyTMakeEmptyT87 MakeEmptyT(MakeEmptyT &&) { throw 42; }
operator =MakeEmptyT88 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
operator =MakeEmptyT89 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
~MakeEmptyTMakeEmptyT90 ~MakeEmptyT() { --alive; }
91 };
92
93 int MakeEmptyT::alive = 0;
94
makeEmpty(Variant & v)95 template <class Variant> void makeEmpty(Variant &v) {
96 Variant v2(std::in_place_type<MakeEmptyT>);
97 try {
98 v = std::move(v2);
99 assert(false);
100 } catch (...) {
101 assert(v.valueless_by_exception());
102 }
103 }
104 #endif // TEST_HAS_NO_EXCEPTIONS
105
test_move_noexcept()106 void test_move_noexcept() {
107 {
108 using V = std::variant<int, long>;
109 static_assert(std::is_nothrow_move_constructible<V>::value, "");
110 }
111 {
112 using V = std::variant<int, MoveOnly>;
113 static_assert(std::is_nothrow_move_constructible<V>::value, "");
114 }
115 {
116 using V = std::variant<int, MoveOnlyNT>;
117 static_assert(!std::is_nothrow_move_constructible<V>::value, "");
118 }
119 {
120 using V = std::variant<int, ThrowsMove>;
121 static_assert(!std::is_nothrow_move_constructible<V>::value, "");
122 }
123 }
124
test_move_ctor_sfinae()125 void test_move_ctor_sfinae() {
126 {
127 using V = std::variant<int, long>;
128 static_assert(std::is_move_constructible<V>::value, "");
129 }
130 {
131 using V = std::variant<int, MoveOnly>;
132 static_assert(std::is_move_constructible<V>::value, "");
133 }
134 {
135 using V = std::variant<int, MoveOnlyNT>;
136 static_assert(std::is_move_constructible<V>::value, "");
137 }
138 {
139 using V = std::variant<int, NoCopy>;
140 static_assert(!std::is_move_constructible<V>::value, "");
141 }
142
143 // Make sure we properly propagate triviality (see P0602R4).
144 #if TEST_STD_VER > 17
145 {
146 using V = std::variant<int, long>;
147 static_assert(std::is_trivially_move_constructible<V>::value, "");
148 }
149 {
150 using V = std::variant<int, NTMove>;
151 static_assert(!std::is_trivially_move_constructible<V>::value, "");
152 static_assert(std::is_move_constructible<V>::value, "");
153 }
154 {
155 using V = std::variant<int, TMove>;
156 static_assert(std::is_trivially_move_constructible<V>::value, "");
157 }
158 {
159 using V = std::variant<int, TMoveNTCopy>;
160 static_assert(std::is_trivially_move_constructible<V>::value, "");
161 }
162 #endif // > C++17
163 }
164
165 template <typename T>
166 struct Result { size_t index; T value; };
167
test_move_ctor_basic()168 void test_move_ctor_basic() {
169 {
170 std::variant<int> v(std::in_place_index<0>, 42);
171 std::variant<int> v2 = std::move(v);
172 assert(v2.index() == 0);
173 assert(std::get<0>(v2) == 42);
174 }
175 {
176 std::variant<int, long> v(std::in_place_index<1>, 42);
177 std::variant<int, long> v2 = std::move(v);
178 assert(v2.index() == 1);
179 assert(std::get<1>(v2) == 42);
180 }
181 {
182 std::variant<MoveOnly> v(std::in_place_index<0>, 42);
183 assert(v.index() == 0);
184 std::variant<MoveOnly> v2(std::move(v));
185 assert(v2.index() == 0);
186 assert(std::get<0>(v2).value == 42);
187 }
188 {
189 std::variant<int, MoveOnly> v(std::in_place_index<1>, 42);
190 assert(v.index() == 1);
191 std::variant<int, MoveOnly> v2(std::move(v));
192 assert(v2.index() == 1);
193 assert(std::get<1>(v2).value == 42);
194 }
195 {
196 std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42);
197 assert(v.index() == 0);
198 std::variant<MoveOnlyNT> v2(std::move(v));
199 assert(v2.index() == 0);
200 assert(std::get<0>(v).value == -1);
201 assert(std::get<0>(v2).value == 42);
202 }
203 {
204 std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42);
205 assert(v.index() == 1);
206 std::variant<int, MoveOnlyNT> v2(std::move(v));
207 assert(v2.index() == 1);
208 assert(std::get<1>(v).value == -1);
209 assert(std::get<1>(v2).value == 42);
210 }
211
212 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
213 #if TEST_STD_VER > 17
214 {
215 struct {
216 constexpr Result<int> operator()() const {
217 std::variant<int> v(std::in_place_index<0>, 42);
218 std::variant<int> v2 = std::move(v);
219 return {v2.index(), std::get<0>(std::move(v2))};
220 }
221 } test;
222 constexpr auto result = test();
223 static_assert(result.index == 0, "");
224 static_assert(result.value == 42, "");
225 }
226 {
227 struct {
228 constexpr Result<long> operator()() const {
229 std::variant<int, long> v(std::in_place_index<1>, 42);
230 std::variant<int, long> v2 = std::move(v);
231 return {v2.index(), std::get<1>(std::move(v2))};
232 }
233 } test;
234 constexpr auto result = test();
235 static_assert(result.index == 1, "");
236 static_assert(result.value == 42, "");
237 }
238 {
239 struct {
240 constexpr Result<TMove> operator()() const {
241 std::variant<TMove> v(std::in_place_index<0>, 42);
242 std::variant<TMove> v2(std::move(v));
243 return {v2.index(), std::get<0>(std::move(v2))};
244 }
245 } test;
246 constexpr auto result = test();
247 static_assert(result.index == 0, "");
248 static_assert(result.value.value == 42, "");
249 }
250 {
251 struct {
252 constexpr Result<TMove> operator()() const {
253 std::variant<int, TMove> v(std::in_place_index<1>, 42);
254 std::variant<int, TMove> v2(std::move(v));
255 return {v2.index(), std::get<1>(std::move(v2))};
256 }
257 } test;
258 constexpr auto result = test();
259 static_assert(result.index == 1, "");
260 static_assert(result.value.value == 42, "");
261 }
262 {
263 struct {
264 constexpr Result<TMoveNTCopy> operator()() const {
265 std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42);
266 std::variant<TMoveNTCopy> v2(std::move(v));
267 return {v2.index(), std::get<0>(std::move(v2))};
268 }
269 } test;
270 constexpr auto result = test();
271 static_assert(result.index == 0, "");
272 static_assert(result.value.value == 42, "");
273 }
274 {
275 struct {
276 constexpr Result<TMoveNTCopy> operator()() const {
277 std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42);
278 std::variant<int, TMoveNTCopy> v2(std::move(v));
279 return {v2.index(), std::get<1>(std::move(v2))};
280 }
281 } test;
282 constexpr auto result = test();
283 static_assert(result.index == 1, "");
284 static_assert(result.value.value == 42, "");
285 }
286 #endif // > C++17
287 }
288
test_move_ctor_valueless_by_exception()289 void test_move_ctor_valueless_by_exception() {
290 #ifndef TEST_HAS_NO_EXCEPTIONS
291 using V = std::variant<int, MakeEmptyT>;
292 V v1;
293 makeEmpty(v1);
294 V v(std::move(v1));
295 assert(v.valueless_by_exception());
296 #endif // TEST_HAS_NO_EXCEPTIONS
297 }
298
299 template <size_t Idx>
test_constexpr_ctor_imp(std::variant<long,void *,const int> const & v)300 constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) {
301 auto copy = v;
302 auto v2 = std::move(copy);
303 return v2.index() == v.index() &&
304 v2.index() == Idx &&
305 std::get<Idx>(v2) == std::get<Idx>(v);
306 }
307
test_constexpr_move_ctor()308 void test_constexpr_move_ctor() {
309 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
310 #if TEST_STD_VER > 17
311 using V = std::variant<long, void*, const int>;
312 #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
313 static_assert(std::is_trivially_destructible<V>::value, "");
314 static_assert(std::is_trivially_copy_constructible<V>::value, "");
315 static_assert(std::is_trivially_move_constructible<V>::value, "");
316 static_assert(!std::is_copy_assignable<V>::value, "");
317 static_assert(!std::is_move_assignable<V>::value, "");
318 #else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
319 static_assert(std::is_trivially_copyable<V>::value, "");
320 #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
321 static_assert(std::is_trivially_move_constructible<V>::value, "");
322 static_assert(test_constexpr_ctor_imp<0>(V(42l)), "");
323 static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), "");
324 static_assert(test_constexpr_ctor_imp<2>(V(101)), "");
325 #endif // > C++17
326 }
327
main(int,char **)328 int main(int, char**) {
329 test_move_ctor_basic();
330 test_move_ctor_valueless_by_exception();
331 test_move_noexcept();
332 test_move_ctor_sfinae();
333 test_constexpr_move_ctor();
334
335 return 0;
336 }
337