xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit.member/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, c++17, c++20, c++23
10 // The tested functionality needs deducing this.
11 // UNSUPPORTED: clang-16 || clang-17
12 // XFAIL: apple-clang
13 
14 // <variant>
15 
16 // class variant;
17 
18 // template<class Self, class Visitor>
19 //   constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
20 
21 #include <cassert>
22 #include <memory>
23 #include <string>
24 #include <tuple>
25 #include <type_traits>
26 #include <utility>
27 #include <variant>
28 
29 #include "test_macros.h"
30 #include "variant_test_helpers.h"
31 
32 void test_call_operator_forwarding() {
33   using Fn = ForwardingCallObject;
34   Fn obj{};
35   const Fn& cobj = obj;
36 
37   { // test call operator forwarding - single variant, single arg
38     using V = std::variant<int>;
39     V v(42);
40 
41     v.visit(obj);
42     assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
43     v.visit(cobj);
44     assert(Fn::check_call<int&>(CT_Const | CT_LValue));
45     v.visit(std::move(obj));
46     assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
47     v.visit(std::move(cobj));
48     assert(Fn::check_call<int&>(CT_Const | CT_RValue));
49   }
50   { // test call operator forwarding - single variant, multi arg
51     using V = std::variant<int, long, double>;
52     V v(42L);
53 
54     v.visit(obj);
55     assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
56     v.visit(cobj);
57     assert(Fn::check_call<long&>(CT_Const | CT_LValue));
58     v.visit(std::move(obj));
59     assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
60     v.visit(std::move(cobj));
61     assert(Fn::check_call<long&>(CT_Const | CT_RValue));
62   }
63 }
64 
65 // Applies to non-member `std::visit` only.
66 void test_argument_forwarding() {
67   using Fn = ForwardingCallObject;
68   Fn obj{};
69   const auto val = CT_LValue | CT_NonConst;
70 
71   { // single argument - value type
72     using V = std::variant<int>;
73     V v(42);
74     const V& cv = v;
75 
76     v.visit(obj);
77     assert(Fn::check_call<int&>(val));
78     cv.visit(obj);
79     assert(Fn::check_call<const int&>(val));
80     std::move(v).visit(obj);
81     assert(Fn::check_call<int&&>(val));
82     std::move(cv).visit(obj);
83     assert(Fn::check_call<const int&&>(val));
84   }
85 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
86   { // single argument - lvalue reference
87     using V = std::variant<int&>;
88     int x   = 42;
89     V v(x);
90     const V& cv = v;
91 
92     v.visit(obj);
93     assert(Fn::check_call<int&>(val));
94     cv.visit(obj);
95     assert(Fn::check_call<int&>(val));
96     std::move(v).visit(obj);
97     assert(Fn::check_call<int&>(val));
98     std::move(cv).visit(obj);
99     assert(Fn::check_call<int&>(val));
100     assert(false);
101   }
102   { // single argument - rvalue reference
103     using V = std::variant<int&&>;
104     int x   = 42;
105     V v(std::move(x));
106     const V& cv = v;
107 
108     v.visit(obj);
109     assert(Fn::check_call<int&>(val));
110     cvstd::visit(obj);
111     assert(Fn::check_call<int&>(val));
112     std::move(v).visit(obj);
113     assert(Fn::check_call<int&&>(val));
114     std::move(cv).visit(obj);
115     assert(Fn::check_call<int&&>(val));
116   }
117 #endif
118 }
119 
120 void test_return_type() {
121   using Fn = ForwardingCallObject;
122   Fn obj{};
123   const Fn& cobj = obj;
124 
125   { // test call operator forwarding - single variant, single arg
126     using V = std::variant<int>;
127     V v(42);
128 
129     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
130     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
131     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
132     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
133   }
134   { // test call operator forwarding - single variant, multi arg
135     using V = std::variant<int, long, double>;
136     V v(42L);
137 
138     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
139     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
140     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
141     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
142   }
143 }
144 
145 void test_constexpr() {
146   constexpr ReturnFirst obj{};
147 
148   {
149     using V = std::variant<int>;
150     constexpr V v(42);
151 
152     static_assert(v.visit(obj) == 42);
153   }
154   {
155     using V = std::variant<short, long, char>;
156     constexpr V v(42L);
157 
158     static_assert(v.visit(obj) == 42);
159   }
160 }
161 
162 void test_exceptions() {
163 #ifndef TEST_HAS_NO_EXCEPTIONS
164   ReturnArity obj{};
165 
166   auto test = [&](auto&& v) {
167     try {
168       v.visit(obj);
169     } catch (const std::bad_variant_access&) {
170       return true;
171     } catch (...) {
172     }
173     return false;
174   };
175 
176   {
177     using V = std::variant<int, MakeEmptyT>;
178     V v;
179     makeEmpty(v);
180 
181     assert(test(v));
182   }
183 #endif
184 }
185 
186 // See https://llvm.org/PR31916
187 void test_caller_accepts_nonconst() {
188   struct A {};
189   struct Visitor {
190     void operator()(A&) {}
191   };
192   std::variant<A> v;
193 
194   v.visit(Visitor{});
195 }
196 
197 struct MyVariant : std::variant<short, long, float> {};
198 
199 namespace std {
200 template <std::size_t Index>
201 void get(const MyVariant&) {
202   assert(false);
203 }
204 } // namespace std
205 
206 void test_derived_from_variant() {
207   auto v1        = MyVariant{42};
208   const auto cv1 = MyVariant{142};
209 
210   v1.visit([](auto x) { assert(x == 42); });
211   cv1.visit([](auto x) { assert(x == 142); });
212   MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); });
213   std::move(v1).visit([](auto x) { assert(x == 42); });
214   std::move(cv1).visit([](auto x) { assert(x == 142); });
215 
216   // Check that visit does not take index nor valueless_by_exception members from the base class.
217   struct EvilVariantBase {
218     int index;
219     char valueless_by_exception;
220   };
221 
222   struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {
223     using std::variant<int, long, double>::variant;
224   };
225 
226   EvilVariant1{12}.visit([](auto x) { assert(x == 12); });
227   EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); });
228 
229   // Check that visit unambiguously picks the variant, even if the other base has __impl member.
230   struct ImplVariantBase {
231     struct Callable {
232       bool operator()() const {
233         assert(false);
234         return false;
235       }
236     };
237 
238     Callable __impl;
239   };
240 
241   struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
242     using std::variant<int, long, double>::variant;
243   };
244 
245   EvilVariant2{12}.visit([](auto x) { assert(x == 12); });
246   EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); });
247 }
248 
249 int main(int, char**) {
250   test_call_operator_forwarding();
251   test_argument_forwarding();
252   test_return_type();
253   test_constexpr();
254   test_exceptions();
255   test_caller_accepts_nonconst();
256   test_derived_from_variant();
257 
258   return 0;
259 }
260