xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp (revision 5dfdac74cadd9483a66eb17e51dc632b554cccb1)
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(v1), std::as_const(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(v1), std::as_const(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(v1), std::as_const(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{}, v);
313 }
314 
315 struct MyVariant : std::variant<short, long, float> {};
316 
317 // FIXME: This is UB according to [namespace.std]
318 namespace std {
319 template <std::size_t Index>
320 void get(const MyVariant&) {
321   assert(false);
322 }
323 } // namespace std
324 
325 void test_derived_from_variant() {
326   auto v1 = MyVariant{42};
327   const auto cv1 = MyVariant{142};
328   std::visit([](auto x) { assert(x == 42); }, v1);
329   std::visit([](auto x) { assert(x == 142); }, cv1);
330   std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f});
331   std::visit([](auto x) { assert(x == 42); }, std::move(v1));
332   std::visit([](auto x) { assert(x == 142); }, std::move(cv1));
333 
334   // Check that visit does not take index nor valueless_by_exception members from the base class.
335   struct EvilVariantBase {
336     int index;
337     char valueless_by_exception;
338   };
339 
340   struct EvilVariant1 : std::variant<int, long, double>,
341                         std::tuple<int>,
342                         EvilVariantBase {
343     using std::variant<int, long, double>::variant;
344   };
345 
346   std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12});
347   std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3});
348 
349   // Check that visit unambiguously picks the variant, even if the other base has __impl member.
350   struct ImplVariantBase {
351     struct Callable {
352       bool operator()() const { assert(false); return false; }
353     };
354 
355     Callable __impl;
356   };
357 
358   struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
359     using std::variant<int, long, double>::variant;
360   };
361 
362   std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12});
363   std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3});
364 }
365 
366 struct any_visitor {
367   template <typename T>
368   void operator()(const T&) const {}
369 };
370 
371 template <typename T, typename = decltype(std::visit(
372                           std::declval<any_visitor&>(), std::declval<T>()))>
373 constexpr bool has_visit(int) {
374   return true;
375 }
376 
377 template <typename T>
378 constexpr bool has_visit(...) {
379   return false;
380 }
381 
382 void test_sfinae() {
383   struct BadVariant : std::variant<short>, std::variant<long, float> {};
384   struct BadVariant2 : private std::variant<long, float> {};
385   struct GoodVariant : std::variant<long, float> {};
386   struct GoodVariant2 : GoodVariant {};
387 
388   static_assert(!has_visit<int>(0));
389   static_assert(!has_visit<BadVariant>(0));
390   static_assert(!has_visit<BadVariant2>(0));
391   static_assert(has_visit<std::variant<int>>(0));
392   static_assert(has_visit<GoodVariant>(0));
393   static_assert(has_visit<GoodVariant2>(0));
394 }
395 
396 int main(int, char**) {
397   test_call_operator_forwarding();
398   test_argument_forwarding();
399   test_return_type();
400   test_constexpr();
401   test_exceptions();
402   test_caller_accepts_nonconst();
403   test_derived_from_variant();
404   test_sfinae();
405 
406   return 0;
407 }
408