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 | // template <class Visitor, class... Variants> |
13 | // constexpr see below visit(Visitor&& vis, Variants&&... vars); |
14 | |
15 | #include <cassert> |
16 | #include <memory> |
17 | #include <string> |
18 | #include <tuple> |
19 | #include <type_traits> |
20 | #include <utility> |
21 | #include <variant> |
22 | |
23 | #include "test_macros.h" |
24 | #include "variant_test_helpers.h" |
25 | |
26 | void test_call_operator_forwarding() { |
27 | using Fn = ForwardingCallObject; |
28 | Fn obj{}; |
29 | const Fn &cobj = obj; |
30 | { // test call operator forwarding - no variant |
31 | std::visit(obj); |
32 | assert(Fn::check_call<>(CT_NonConst | CT_LValue)); |
33 | std::visit(cobj); |
34 | assert(Fn::check_call<>(CT_Const | CT_LValue)); |
35 | std::visit(std::move(obj)); |
36 | assert(Fn::check_call<>(CT_NonConst | CT_RValue)); |
37 | std::visit(std::move(cobj)); |
38 | assert(Fn::check_call<>(CT_Const | CT_RValue)); |
39 | } |
40 | { // test call operator forwarding - single variant, single arg |
41 | using V = std::variant<int>; |
42 | V v(42); |
43 | std::visit(obj, v); |
44 | assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); |
45 | std::visit(cobj, v); |
46 | assert(Fn::check_call<int &>(CT_Const | CT_LValue)); |
47 | std::visit(std::move(obj), v); |
48 | assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); |
49 | std::visit(std::move(cobj), v); |
50 | assert(Fn::check_call<int &>(CT_Const | CT_RValue)); |
51 | } |
52 | { // test call operator forwarding - single variant, multi arg |
53 | using V = std::variant<int, long, double>; |
54 | V v(42l); |
55 | std::visit(obj, v); |
56 | assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); |
57 | std::visit(cobj, v); |
58 | assert(Fn::check_call<long &>(CT_Const | CT_LValue)); |
59 | std::visit(std::move(obj), v); |
60 | assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); |
61 | std::visit(std::move(cobj), v); |
62 | assert(Fn::check_call<long &>(CT_Const | CT_RValue)); |
63 | } |
64 | { // test call operator forwarding - multi variant, multi arg |
65 | using V = std::variant<int, long, double>; |
66 | using V2 = std::variant<int *, std::string>; |
67 | V v(42l); |
68 | V2 v2("hello" ); |
69 | std::visit(obj, v, v2); |
70 | assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); |
71 | std::visit(cobj, v, v2); |
72 | assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); |
73 | std::visit(std::move(obj), v, v2); |
74 | assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); |
75 | std::visit(std::move(cobj), v, v2); |
76 | assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); |
77 | } |
78 | { |
79 | using V = std::variant<int, long, double, std::string>; |
80 | V v1(42l), v2("hello" ), v3(101), v4(1.1); |
81 | std::visit(obj, v1, v2, v3, v4); |
82 | assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue))); |
83 | std::visit(cobj, v1, v2, v3, v4); |
84 | assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue))); |
85 | std::visit(std::move(obj), v1, v2, v3, v4); |
86 | assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue))); |
87 | std::visit(std::move(cobj), v1, v2, v3, v4); |
88 | assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue))); |
89 | } |
90 | { |
91 | using V = std::variant<int, long, double, int*, std::string>; |
92 | V v1(42l), v2("hello" ), v3(nullptr), v4(1.1); |
93 | std::visit(obj, v1, v2, v3, v4); |
94 | assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue))); |
95 | std::visit(cobj, v1, v2, v3, v4); |
96 | assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue))); |
97 | std::visit(std::move(obj), v1, v2, v3, v4); |
98 | assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue))); |
99 | std::visit(std::move(cobj), v1, v2, v3, v4); |
100 | assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue))); |
101 | } |
102 | } |
103 | |
104 | void test_argument_forwarding() { |
105 | using Fn = ForwardingCallObject; |
106 | Fn obj{}; |
107 | const auto Val = CT_LValue | CT_NonConst; |
108 | { // single argument - value type |
109 | using V = std::variant<int>; |
110 | V v(42); |
111 | const V &cv = v; |
112 | std::visit(obj, v); |
113 | assert(Fn::check_call<int &>(Val)); |
114 | std::visit(obj, cv); |
115 | assert(Fn::check_call<const int &>(Val)); |
116 | std::visit(obj, std::move(v)); |
117 | assert(Fn::check_call<int &&>(Val)); |
118 | std::visit(obj, std::move(cv)); |
119 | assert(Fn::check_call<const int &&>(Val)); |
120 | } |
121 | { // multi argument - multi variant |
122 | using V = std::variant<int, std::string, long>; |
123 | V v1(42), v2("hello" ), v3(43l); |
124 | std::visit(obj, v1, v2, v3); |
125 | assert((Fn::check_call<int &, std::string &, long &>(Val))); |
126 | std::visit(obj, std::as_const(t&: v1), std::as_const(t&: v2), std::move(v3)); |
127 | assert((Fn::check_call<const int &, const std::string &, long &&>(Val))); |
128 | } |
129 | { |
130 | using V = std::variant<int, long, double, std::string>; |
131 | V v1(42l), v2("hello" ), v3(101), v4(1.1); |
132 | std::visit(obj, v1, v2, v3, v4); |
133 | assert((Fn::check_call<long &, std::string &, int &, double &>(Val))); |
134 | std::visit(obj, std::as_const(t&: v1), std::as_const(t&: v2), std::move(v3), std::move(v4)); |
135 | assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val))); |
136 | } |
137 | { |
138 | using V = std::variant<int, long, double, int*, std::string>; |
139 | V v1(42l), v2("hello" ), v3(nullptr), v4(1.1); |
140 | std::visit(obj, v1, v2, v3, v4); |
141 | assert((Fn::check_call<long &, std::string &, int *&, double &>(Val))); |
142 | std::visit(obj, std::as_const(t&: v1), std::as_const(t&: v2), std::move(v3), std::move(v4)); |
143 | assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val))); |
144 | } |
145 | } |
146 | |
147 | void test_return_type() { |
148 | using Fn = ForwardingCallObject; |
149 | Fn obj{}; |
150 | const Fn &cobj = obj; |
151 | { // test call operator forwarding - no variant |
152 | static_assert(std::is_same_v<decltype(std::visit(obj)), Fn&>); |
153 | static_assert(std::is_same_v<decltype(std::visit(cobj)), const Fn&>); |
154 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj))), Fn&&>); |
155 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj))), const Fn&&>); |
156 | } |
157 | { // test call operator forwarding - single variant, single arg |
158 | using V = std::variant<int>; |
159 | V v(42); |
160 | static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>); |
161 | static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>); |
162 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>); |
163 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>); |
164 | } |
165 | { // test call operator forwarding - single variant, multi arg |
166 | using V = std::variant<int, long, double>; |
167 | V v(42l); |
168 | static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>); |
169 | static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>); |
170 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>); |
171 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>); |
172 | } |
173 | { // test call operator forwarding - multi variant, multi arg |
174 | using V = std::variant<int, long, double>; |
175 | using V2 = std::variant<int *, std::string>; |
176 | V v(42l); |
177 | V2 v2("hello" ); |
178 | static_assert(std::is_same_v<decltype(std::visit(obj, v, v2)), Fn&>); |
179 | static_assert(std::is_same_v<decltype(std::visit(cobj, v, v2)), const Fn&>); |
180 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v, v2)), Fn&&>); |
181 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v, v2)), const Fn&&>); |
182 | } |
183 | { |
184 | using V = std::variant<int, long, double, std::string>; |
185 | V v1(42l), v2("hello" ), v3(101), v4(1.1); |
186 | static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>); |
187 | static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>); |
188 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>); |
189 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>); |
190 | } |
191 | { |
192 | using V = std::variant<int, long, double, int*, std::string>; |
193 | V v1(42l), v2("hello" ), v3(nullptr), v4(1.1); |
194 | static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>); |
195 | static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>); |
196 | static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>); |
197 | static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>); |
198 | } |
199 | } |
200 | |
201 | void test_constexpr() { |
202 | constexpr ReturnFirst obj{}; |
203 | constexpr ReturnArity aobj{}; |
204 | { |
205 | using V = std::variant<int>; |
206 | constexpr V v(42); |
207 | static_assert(std::visit(obj, v) == 42, "" ); |
208 | } |
209 | { |
210 | using V = std::variant<short, long, char>; |
211 | constexpr V v(42l); |
212 | static_assert(std::visit(obj, v) == 42, "" ); |
213 | } |
214 | { |
215 | using V1 = std::variant<int>; |
216 | using V2 = std::variant<int, char *, long long>; |
217 | using V3 = std::variant<bool, int, int>; |
218 | constexpr V1 v1; |
219 | constexpr V2 v2(nullptr); |
220 | constexpr V3 v3; |
221 | static_assert(std::visit(aobj, v1, v2, v3) == 3, "" ); |
222 | } |
223 | { |
224 | using V1 = std::variant<int>; |
225 | using V2 = std::variant<int, char *, long long>; |
226 | using V3 = std::variant<void *, int, int>; |
227 | constexpr V1 v1; |
228 | constexpr V2 v2(nullptr); |
229 | constexpr V3 v3; |
230 | static_assert(std::visit(aobj, v1, v2, v3) == 3, "" ); |
231 | } |
232 | { |
233 | using V = std::variant<int, long, double, int *>; |
234 | constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); |
235 | static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "" ); |
236 | } |
237 | { |
238 | using V = std::variant<int, long, double, long long, int *>; |
239 | constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); |
240 | static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "" ); |
241 | } |
242 | } |
243 | |
244 | void test_exceptions() { |
245 | #ifndef TEST_HAS_NO_EXCEPTIONS |
246 | ReturnArity obj{}; |
247 | auto test = [&](auto &&... args) { |
248 | try { |
249 | std::visit(obj, args...); |
250 | } catch (const std::bad_variant_access &) { |
251 | return true; |
252 | } catch (...) { |
253 | } |
254 | return false; |
255 | }; |
256 | { |
257 | using V = std::variant<int, MakeEmptyT>; |
258 | V v; |
259 | makeEmpty(v); |
260 | assert(test(v)); |
261 | } |
262 | { |
263 | using V = std::variant<int, MakeEmptyT>; |
264 | using V2 = std::variant<long, std::string, void *>; |
265 | V v; |
266 | makeEmpty(v); |
267 | V2 v2("hello" ); |
268 | assert(test(v, v2)); |
269 | } |
270 | { |
271 | using V = std::variant<int, MakeEmptyT>; |
272 | using V2 = std::variant<long, std::string, void *>; |
273 | V v; |
274 | makeEmpty(v); |
275 | V2 v2("hello" ); |
276 | assert(test(v2, v)); |
277 | } |
278 | { |
279 | using V = std::variant<int, MakeEmptyT>; |
280 | using V2 = std::variant<long, std::string, void *, MakeEmptyT>; |
281 | V v; |
282 | makeEmpty(v); |
283 | V2 v2; |
284 | makeEmpty(v2); |
285 | assert(test(v, v2)); |
286 | } |
287 | { |
288 | using V = std::variant<int, long, double, MakeEmptyT>; |
289 | V v1(42l), v2(101), v3(202), v4(1.1); |
290 | makeEmpty(v1); |
291 | assert(test(v1, v2, v3, v4)); |
292 | } |
293 | { |
294 | using V = std::variant<int, long, double, long long, MakeEmptyT>; |
295 | V v1(42l), v2(101), v3(202), v4(1.1); |
296 | makeEmpty(v1); |
297 | makeEmpty(v2); |
298 | makeEmpty(v3); |
299 | makeEmpty(v4); |
300 | assert(test(v1, v2, v3, v4)); |
301 | } |
302 | #endif |
303 | } |
304 | |
305 | // See https://llvm.org/PR31916 |
306 | void test_caller_accepts_nonconst() { |
307 | struct A {}; |
308 | struct Visitor { |
309 | void operator()(A&) {} |
310 | }; |
311 | std::variant<A> v; |
312 | std::visit(visitor: Visitor{}, variants&: v); |
313 | } |
314 | |
315 | struct MyVariant : std::variant<short, long, float> {}; |
316 | |
317 | namespace std { |
318 | template <std::size_t Index> |
319 | void get(const MyVariant&) { |
320 | assert(false); |
321 | } |
322 | } // namespace std |
323 | |
324 | void test_derived_from_variant() { |
325 | auto v1 = MyVariant{42}; |
326 | const auto cv1 = MyVariant{142}; |
327 | std::visit(visitor: [](auto x) { assert(x == 42); }, variants&: v1); |
328 | std::visit(visitor: [](auto x) { assert(x == 142); }, variants: cv1); |
329 | std::visit(visitor: [](auto x) { assert(x == -1.25f); }, variants: MyVariant{-1.25f}); |
330 | std::visit(visitor: [](auto x) { assert(x == 42); }, variants: std::move(v1)); |
331 | std::visit(visitor: [](auto x) { assert(x == 142); }, variants: std::move(cv1)); |
332 | |
333 | // Check that visit does not take index nor valueless_by_exception members from the base class. |
334 | struct EvilVariantBase { |
335 | int index; |
336 | char valueless_by_exception; |
337 | }; |
338 | |
339 | struct EvilVariant1 : std::variant<int, long, double>, |
340 | std::tuple<int>, |
341 | EvilVariantBase { |
342 | using std::variant<int, long, double>::variant; |
343 | }; |
344 | |
345 | std::visit(visitor: [](auto x) { assert(x == 12); }, variants: EvilVariant1{12}); |
346 | std::visit(visitor: [](auto x) { assert(x == 12.3); }, variants: EvilVariant1{12.3}); |
347 | |
348 | // Check that visit unambiguously picks the variant, even if the other base has __impl member. |
349 | struct ImplVariantBase { |
350 | struct Callable { |
351 | bool operator()() const { assert(false); return false; } |
352 | }; |
353 | |
354 | Callable __impl; |
355 | }; |
356 | |
357 | struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase { |
358 | using std::variant<int, long, double>::variant; |
359 | }; |
360 | |
361 | std::visit(visitor: [](auto x) { assert(x == 12); }, variants: EvilVariant2{12}); |
362 | std::visit(visitor: [](auto x) { assert(x == 12.3); }, variants: EvilVariant2{12.3}); |
363 | } |
364 | |
365 | struct any_visitor { |
366 | template <typename T> |
367 | void operator()(const T&) const {} |
368 | }; |
369 | |
370 | template <typename T, typename = decltype(std::visit( |
371 | std::declval<any_visitor&>(), std::declval<T>()))> |
372 | constexpr bool has_visit(int) { |
373 | return true; |
374 | } |
375 | |
376 | template <typename T> |
377 | constexpr bool has_visit(...) { |
378 | return false; |
379 | } |
380 | |
381 | void test_sfinae() { |
382 | struct BadVariant : std::variant<short>, std::variant<long, float> {}; |
383 | struct BadVariant2 : private std::variant<long, float> {}; |
384 | struct GoodVariant : std::variant<long, float> {}; |
385 | struct GoodVariant2 : GoodVariant {}; |
386 | |
387 | static_assert(!has_visit<int>(0)); |
388 | static_assert(!has_visit<BadVariant>(0)); |
389 | static_assert(!has_visit<BadVariant2>(0)); |
390 | static_assert(has_visit<std::variant<int>>(0)); |
391 | static_assert(has_visit<GoodVariant>(0)); |
392 | static_assert(has_visit<GoodVariant2>(0)); |
393 | } |
394 | |
395 | int main(int, char**) { |
396 | test_call_operator_forwarding(); |
397 | test_argument_forwarding(); |
398 | test_return_type(); |
399 | test_constexpr(); |
400 | test_exceptions(); |
401 | test_caller_accepts_nonconst(); |
402 | test_derived_from_variant(); |
403 | test_sfinae(); |
404 | |
405 | return 0; |
406 | } |
407 | |