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
26void 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
104void 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
147void 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
201void 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
244void 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
306void 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
315struct MyVariant : std::variant<short, long, float> {};
316
317namespace std {
318template <std::size_t Index>
319void get(const MyVariant&) {
320 assert(false);
321}
322} // namespace std
323
324void 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
365struct any_visitor {
366 template <typename T>
367 void operator()(const T&) const {}
368};
369
370template <typename T, typename = decltype(std::visit(
371 std::declval<any_visitor&>(), std::declval<T>()))>
372constexpr bool has_visit(int) {
373 return true;
374}
375
376template <typename T>
377constexpr bool has_visit(...) {
378 return false;
379}
380
381void 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
395int 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

source code of libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp