xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp (revision 2a38551457cb2b38dcca35e30e9f2d7fce9ae3e7)
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 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
122   { // single argument - lvalue reference
123     using V = std::variant<int &>;
124     int x = 42;
125     V v(x);
126     const V &cv = v;
127     std::visit(obj, v);
128     assert(Fn::check_call<int &>(Val));
129     std::visit(obj, cv);
130     assert(Fn::check_call<int &>(Val));
131     std::visit(obj, std::move(v));
132     assert(Fn::check_call<int &>(Val));
133     std::visit(obj, std::move(cv));
134     assert(Fn::check_call<int &>(Val));
135   }
136   { // single argument - rvalue reference
137     using V = std::variant<int &&>;
138     int x = 42;
139     V v(std::move(x));
140     const V &cv = v;
141     std::visit(obj, v);
142     assert(Fn::check_call<int &>(Val));
143     std::visit(obj, cv);
144     assert(Fn::check_call<int &>(Val));
145     std::visit(obj, std::move(v));
146     assert(Fn::check_call<int &&>(Val));
147     std::visit(obj, std::move(cv));
148     assert(Fn::check_call<int &&>(Val));
149   }
150 #endif
151   { // multi argument - multi variant
152     using V = std::variant<int, std::string, long>;
153     V v1(42), v2("hello"), v3(43l);
154     std::visit(obj, v1, v2, v3);
155     assert((Fn::check_call<int &, std::string &, long &>(Val)));
156     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3));
157     assert((Fn::check_call<const int &, const std::string &, long &&>(Val)));
158   }
159   {
160     using V = std::variant<int, long, double, std::string>;
161     V v1(42l), v2("hello"), v3(101), v4(1.1);
162     std::visit(obj, v1, v2, v3, v4);
163     assert((Fn::check_call<long &, std::string &, int &, double &>(Val)));
164     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
165     assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val)));
166   }
167   {
168     using V = std::variant<int, long, double, int*, std::string>;
169     V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
170     std::visit(obj, v1, v2, v3, v4);
171     assert((Fn::check_call<long &, std::string &, int *&, double &>(Val)));
172     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
173     assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val)));
174   }
175 }
176 
177 void test_return_type() {
178   using Fn = ForwardingCallObject;
179   Fn obj{};
180   const Fn &cobj = obj;
181   { // test call operator forwarding - no variant
182     static_assert(std::is_same_v<decltype(std::visit(obj)), Fn&>);
183     static_assert(std::is_same_v<decltype(std::visit(cobj)), const Fn&>);
184     static_assert(std::is_same_v<decltype(std::visit(std::move(obj))), Fn&&>);
185     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj))), const Fn&&>);
186   }
187   { // test call operator forwarding - single variant, single arg
188     using V = std::variant<int>;
189     V v(42);
190     static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);
191     static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);
192     static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);
193     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);
194   }
195   { // test call operator forwarding - single variant, multi arg
196     using V = std::variant<int, long, double>;
197     V v(42l);
198     static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);
199     static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);
200     static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);
201     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);
202   }
203   { // test call operator forwarding - multi variant, multi arg
204     using V = std::variant<int, long, double>;
205     using V2 = std::variant<int *, std::string>;
206     V v(42l);
207     V2 v2("hello");
208     static_assert(std::is_same_v<decltype(std::visit(obj, v, v2)), Fn&>);
209     static_assert(std::is_same_v<decltype(std::visit(cobj, v, v2)), const Fn&>);
210     static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v, v2)), Fn&&>);
211     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v, v2)), const Fn&&>);
212   }
213   {
214     using V = std::variant<int, long, double, std::string>;
215     V v1(42l), v2("hello"), v3(101), v4(1.1);
216     static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);
217     static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);
218     static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);
219     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);
220   }
221   {
222     using V = std::variant<int, long, double, int*, std::string>;
223     V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
224     static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);
225     static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);
226     static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);
227     static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);
228   }
229 }
230 
231 void test_constexpr() {
232   constexpr ReturnFirst obj{};
233   constexpr ReturnArity aobj{};
234   {
235     using V = std::variant<int>;
236     constexpr V v(42);
237     static_assert(std::visit(obj, v) == 42, "");
238   }
239   {
240     using V = std::variant<short, long, char>;
241     constexpr V v(42l);
242     static_assert(std::visit(obj, v) == 42, "");
243   }
244   {
245     using V1 = std::variant<int>;
246     using V2 = std::variant<int, char *, long long>;
247     using V3 = std::variant<bool, int, int>;
248     constexpr V1 v1;
249     constexpr V2 v2(nullptr);
250     constexpr V3 v3;
251     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
252   }
253   {
254     using V1 = std::variant<int>;
255     using V2 = std::variant<int, char *, long long>;
256     using V3 = std::variant<void *, int, int>;
257     constexpr V1 v1;
258     constexpr V2 v2(nullptr);
259     constexpr V3 v3;
260     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
261   }
262   {
263     using V = std::variant<int, long, double, int *>;
264     constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
265     static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
266   }
267   {
268     using V = std::variant<int, long, double, long long, int *>;
269     constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
270     static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
271   }
272 }
273 
274 void test_exceptions() {
275 #ifndef TEST_HAS_NO_EXCEPTIONS
276   ReturnArity obj{};
277   auto test = [&](auto &&... args) {
278     try {
279       std::visit(obj, args...);
280     } catch (const std::bad_variant_access &) {
281       return true;
282     } catch (...) {
283     }
284     return false;
285   };
286   {
287     using V = std::variant<int, MakeEmptyT>;
288     V v;
289     makeEmpty(v);
290     assert(test(v));
291   }
292   {
293     using V = std::variant<int, MakeEmptyT>;
294     using V2 = std::variant<long, std::string, void *>;
295     V v;
296     makeEmpty(v);
297     V2 v2("hello");
298     assert(test(v, v2));
299   }
300   {
301     using V = std::variant<int, MakeEmptyT>;
302     using V2 = std::variant<long, std::string, void *>;
303     V v;
304     makeEmpty(v);
305     V2 v2("hello");
306     assert(test(v2, v));
307   }
308   {
309     using V = std::variant<int, MakeEmptyT>;
310     using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
311     V v;
312     makeEmpty(v);
313     V2 v2;
314     makeEmpty(v2);
315     assert(test(v, v2));
316   }
317   {
318     using V = std::variant<int, long, double, MakeEmptyT>;
319     V v1(42l), v2(101), v3(202), v4(1.1);
320     makeEmpty(v1);
321     assert(test(v1, v2, v3, v4));
322   }
323   {
324     using V = std::variant<int, long, double, long long, MakeEmptyT>;
325     V v1(42l), v2(101), v3(202), v4(1.1);
326     makeEmpty(v1);
327     makeEmpty(v2);
328     makeEmpty(v3);
329     makeEmpty(v4);
330     assert(test(v1, v2, v3, v4));
331   }
332 #endif
333 }
334 
335 // See https://llvm.org/PR31916
336 void test_caller_accepts_nonconst() {
337   struct A {};
338   struct Visitor {
339     void operator()(A&) {}
340   };
341   std::variant<A> v;
342   std::visit(Visitor{}, v);
343 }
344 
345 struct MyVariant : std::variant<short, long, float> {};
346 
347 namespace std {
348 template <std::size_t Index>
349 void get(const MyVariant&) {
350   assert(false);
351 }
352 } // namespace std
353 
354 void test_derived_from_variant() {
355   auto v1 = MyVariant{42};
356   const auto cv1 = MyVariant{142};
357   std::visit([](auto x) { assert(x == 42); }, v1);
358   std::visit([](auto x) { assert(x == 142); }, cv1);
359   std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f});
360   std::visit([](auto x) { assert(x == 42); }, std::move(v1));
361   std::visit([](auto x) { assert(x == 142); }, std::move(cv1));
362 
363   // Check that visit does not take index nor valueless_by_exception members from the base class.
364   struct EvilVariantBase {
365     int index;
366     char valueless_by_exception;
367   };
368 
369   struct EvilVariant1 : std::variant<int, long, double>,
370                         std::tuple<int>,
371                         EvilVariantBase {
372     using std::variant<int, long, double>::variant;
373   };
374 
375   std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12});
376   std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3});
377 
378   // Check that visit unambiguously picks the variant, even if the other base has __impl member.
379   struct ImplVariantBase {
380     struct Callable {
381       bool operator()() const { assert(false); return false; }
382     };
383 
384     Callable __impl;
385   };
386 
387   struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
388     using std::variant<int, long, double>::variant;
389   };
390 
391   std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12});
392   std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3});
393 }
394 
395 struct any_visitor {
396   template <typename T>
397   void operator()(const T&) const {}
398 };
399 
400 template <typename T, typename = decltype(std::visit(
401                           std::declval<any_visitor&>(), std::declval<T>()))>
402 constexpr bool has_visit(int) {
403   return true;
404 }
405 
406 template <typename T>
407 constexpr bool has_visit(...) {
408   return false;
409 }
410 
411 void test_sfinae() {
412   struct BadVariant : std::variant<short>, std::variant<long, float> {};
413   struct BadVariant2 : private std::variant<long, float> {};
414   struct GoodVariant : std::variant<long, float> {};
415   struct GoodVariant2 : GoodVariant {};
416 
417   static_assert(!has_visit<int>(0));
418   static_assert(!has_visit<BadVariant>(0));
419   static_assert(!has_visit<BadVariant2>(0));
420   static_assert(has_visit<std::variant<int>>(0));
421   static_assert(has_visit<GoodVariant>(0));
422   static_assert(has_visit<GoodVariant2>(0));
423 }
424 
425 int main(int, char**) {
426   test_call_operator_forwarding();
427   test_argument_forwarding();
428   test_return_type();
429   test_constexpr();
430   test_exceptions();
431   test_caller_accepts_nonconst();
432   test_derived_from_variant();
433   test_sfinae();
434 
435   return 0;
436 }
437