1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 // UNSUPPORTED: c++03, c++11, c++14
11
12 // Throwing bad_variant_access is supported starting in macosx10.13
13 // XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions
14 // XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions
15 // XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions
16 // XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions
17
18 // <variant>
19 // template <class Visitor, class... Variants>
20 // constexpr see below visit(Visitor&& vis, Variants&&... vars);
21
22 #include <cassert>
23 #include <memory>
24 #include <string>
25 #include <type_traits>
26 #include <utility>
27 #include <variant>
28
29 #include "test_macros.h"
30 #include "type_id.h"
31 #include "variant_test_helpers.h"
32
33 enum CallType : unsigned {
34 CT_None,
35 CT_NonConst = 1,
36 CT_Const = 2,
37 CT_LValue = 4,
38 CT_RValue = 8
39 };
40
operator |(CallType LHS,CallType RHS)41 inline constexpr CallType operator|(CallType LHS, CallType RHS) {
42 return static_cast<CallType>(static_cast<unsigned>(LHS) |
43 static_cast<unsigned>(RHS));
44 }
45
46 struct ForwardingCallObject {
47
48 template <class... Args>
operator ()ForwardingCallObject49 ForwardingCallObject& operator()(Args&&...) & {
50 set_call<Args &&...>(CT_NonConst | CT_LValue);
51 return *this;
52 }
53
54 template <class... Args>
operator ()ForwardingCallObject55 const ForwardingCallObject& operator()(Args&&...) const & {
56 set_call<Args &&...>(CT_Const | CT_LValue);
57 return *this;
58 }
59
60 template <class... Args>
operator ()ForwardingCallObject61 ForwardingCallObject&& operator()(Args&&...) && {
62 set_call<Args &&...>(CT_NonConst | CT_RValue);
63 return std::move(*this);
64 }
65
66 template <class... Args>
operator ()ForwardingCallObject67 const ForwardingCallObject&& operator()(Args&&...) const && {
68 set_call<Args &&...>(CT_Const | CT_RValue);
69 return std::move(*this);
70 }
71
set_callForwardingCallObject72 template <class... Args> static void set_call(CallType type) {
73 assert(last_call_type == CT_None);
74 assert(last_call_args == nullptr);
75 last_call_type = type;
76 last_call_args = std::addressof(makeArgumentID<Args...>());
77 }
78
check_callForwardingCallObject79 template <class... Args> static bool check_call(CallType type) {
80 bool result = last_call_type == type && last_call_args &&
81 *last_call_args == makeArgumentID<Args...>();
82 last_call_type = CT_None;
83 last_call_args = nullptr;
84 return result;
85 }
86
87 static CallType last_call_type;
88 static const TypeID *last_call_args;
89 };
90
91 CallType ForwardingCallObject::last_call_type = CT_None;
92 const TypeID *ForwardingCallObject::last_call_args = nullptr;
93
test_call_operator_forwarding()94 void test_call_operator_forwarding() {
95 using Fn = ForwardingCallObject;
96 Fn obj{};
97 const Fn &cobj = obj;
98 { // test call operator forwarding - no variant
99 std::visit(obj);
100 assert(Fn::check_call<>(CT_NonConst | CT_LValue));
101 std::visit(cobj);
102 assert(Fn::check_call<>(CT_Const | CT_LValue));
103 std::visit(std::move(obj));
104 assert(Fn::check_call<>(CT_NonConst | CT_RValue));
105 std::visit(std::move(cobj));
106 assert(Fn::check_call<>(CT_Const | CT_RValue));
107 }
108 { // test call operator forwarding - single variant, single arg
109 using V = std::variant<int>;
110 V v(42);
111 std::visit(obj, v);
112 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
113 std::visit(cobj, v);
114 assert(Fn::check_call<int &>(CT_Const | CT_LValue));
115 std::visit(std::move(obj), v);
116 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
117 std::visit(std::move(cobj), v);
118 assert(Fn::check_call<int &>(CT_Const | CT_RValue));
119 }
120 { // test call operator forwarding - single variant, multi arg
121 using V = std::variant<int, long, double>;
122 V v(42l);
123 std::visit(obj, v);
124 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
125 std::visit(cobj, v);
126 assert(Fn::check_call<long &>(CT_Const | CT_LValue));
127 std::visit(std::move(obj), v);
128 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
129 std::visit(std::move(cobj), v);
130 assert(Fn::check_call<long &>(CT_Const | CT_RValue));
131 }
132 { // test call operator forwarding - multi variant, multi arg
133 using V = std::variant<int, long, double>;
134 using V2 = std::variant<int *, std::string>;
135 V v(42l);
136 V2 v2("hello");
137 std::visit(obj, v, v2);
138 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
139 std::visit(cobj, v, v2);
140 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
141 std::visit(std::move(obj), v, v2);
142 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
143 std::visit(std::move(cobj), v, v2);
144 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
145 }
146 {
147 using V = std::variant<int, long, double, std::string>;
148 V v1(42l), v2("hello"), v3(101), v4(1.1);
149 std::visit(obj, v1, v2, v3, v4);
150 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue)));
151 std::visit(cobj, v1, v2, v3, v4);
152 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue)));
153 std::visit(std::move(obj), v1, v2, v3, v4);
154 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue)));
155 std::visit(std::move(cobj), v1, v2, v3, v4);
156 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue)));
157 }
158 {
159 using V = std::variant<int, long, double, int*, std::string>;
160 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
161 std::visit(obj, v1, v2, v3, v4);
162 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue)));
163 std::visit(cobj, v1, v2, v3, v4);
164 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue)));
165 std::visit(std::move(obj), v1, v2, v3, v4);
166 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue)));
167 std::visit(std::move(cobj), v1, v2, v3, v4);
168 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue)));
169 }
170 }
171
test_argument_forwarding()172 void test_argument_forwarding() {
173 using Fn = ForwardingCallObject;
174 Fn obj{};
175 const auto Val = CT_LValue | CT_NonConst;
176 { // single argument - value type
177 using V = std::variant<int>;
178 V v(42);
179 const V &cv = v;
180 std::visit(obj, v);
181 assert(Fn::check_call<int &>(Val));
182 std::visit(obj, cv);
183 assert(Fn::check_call<const int &>(Val));
184 std::visit(obj, std::move(v));
185 assert(Fn::check_call<int &&>(Val));
186 std::visit(obj, std::move(cv));
187 assert(Fn::check_call<const int &&>(Val));
188 }
189 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
190 { // single argument - lvalue reference
191 using V = std::variant<int &>;
192 int x = 42;
193 V v(x);
194 const V &cv = v;
195 std::visit(obj, v);
196 assert(Fn::check_call<int &>(Val));
197 std::visit(obj, cv);
198 assert(Fn::check_call<int &>(Val));
199 std::visit(obj, std::move(v));
200 assert(Fn::check_call<int &>(Val));
201 std::visit(obj, std::move(cv));
202 assert(Fn::check_call<int &>(Val));
203 }
204 { // single argument - rvalue reference
205 using V = std::variant<int &&>;
206 int x = 42;
207 V v(std::move(x));
208 const V &cv = v;
209 std::visit(obj, v);
210 assert(Fn::check_call<int &>(Val));
211 std::visit(obj, cv);
212 assert(Fn::check_call<int &>(Val));
213 std::visit(obj, std::move(v));
214 assert(Fn::check_call<int &&>(Val));
215 std::visit(obj, std::move(cv));
216 assert(Fn::check_call<int &&>(Val));
217 }
218 #endif
219 { // multi argument - multi variant
220 using V = std::variant<int, std::string, long>;
221 V v1(42), v2("hello"), v3(43l);
222 std::visit(obj, v1, v2, v3);
223 assert((Fn::check_call<int &, std::string &, long &>(Val)));
224 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3));
225 assert((Fn::check_call<const int &, const std::string &, long &&>(Val)));
226 }
227 {
228 using V = std::variant<int, long, double, std::string>;
229 V v1(42l), v2("hello"), v3(101), v4(1.1);
230 std::visit(obj, v1, v2, v3, v4);
231 assert((Fn::check_call<long &, std::string &, int &, double &>(Val)));
232 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
233 assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val)));
234 }
235 {
236 using V = std::variant<int, long, double, int*, std::string>;
237 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
238 std::visit(obj, v1, v2, v3, v4);
239 assert((Fn::check_call<long &, std::string &, int *&, double &>(Val)));
240 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
241 assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val)));
242 }
243 }
244
test_return_type()245 void test_return_type() {
246 using Fn = ForwardingCallObject;
247 Fn obj{};
248 const Fn &cobj = obj;
249 { // test call operator forwarding - no variant
250 static_assert(std::is_same_v<decltype(std::visit(obj)), Fn&>);
251 static_assert(std::is_same_v<decltype(std::visit(cobj)), const Fn&>);
252 static_assert(std::is_same_v<decltype(std::visit(std::move(obj))), Fn&&>);
253 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj))), const Fn&&>);
254 }
255 { // test call operator forwarding - single variant, single arg
256 using V = std::variant<int>;
257 V v(42);
258 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);
259 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);
260 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);
261 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);
262 }
263 { // test call operator forwarding - single variant, multi arg
264 using V = std::variant<int, long, double>;
265 V v(42l);
266 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);
267 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);
268 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);
269 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);
270 }
271 { // test call operator forwarding - multi variant, multi arg
272 using V = std::variant<int, long, double>;
273 using V2 = std::variant<int *, std::string>;
274 V v(42l);
275 V2 v2("hello");
276 static_assert(std::is_same_v<decltype(std::visit(obj, v, v2)), Fn&>);
277 static_assert(std::is_same_v<decltype(std::visit(cobj, v, v2)), const Fn&>);
278 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v, v2)), Fn&&>);
279 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v, v2)), const Fn&&>);
280 }
281 {
282 using V = std::variant<int, long, double, std::string>;
283 V v1(42l), v2("hello"), v3(101), v4(1.1);
284 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);
285 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);
286 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);
287 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);
288 }
289 {
290 using V = std::variant<int, long, double, int*, std::string>;
291 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
292 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);
293 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);
294 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);
295 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);
296 }
297 }
298
299 struct ReturnFirst {
operator ()ReturnFirst300 template <class... Args> constexpr int operator()(int f, Args &&...) const {
301 return f;
302 }
303 };
304
305 struct ReturnArity {
operator ()ReturnArity306 template <class... Args> constexpr int operator()(Args &&...) const {
307 return sizeof...(Args);
308 }
309 };
310
test_constexpr()311 void test_constexpr() {
312 constexpr ReturnFirst obj{};
313 constexpr ReturnArity aobj{};
314 {
315 using V = std::variant<int>;
316 constexpr V v(42);
317 static_assert(std::visit(obj, v) == 42, "");
318 }
319 {
320 using V = std::variant<short, long, char>;
321 constexpr V v(42l);
322 static_assert(std::visit(obj, v) == 42, "");
323 }
324 {
325 using V1 = std::variant<int>;
326 using V2 = std::variant<int, char *, long long>;
327 using V3 = std::variant<bool, int, int>;
328 constexpr V1 v1;
329 constexpr V2 v2(nullptr);
330 constexpr V3 v3;
331 static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
332 }
333 {
334 using V1 = std::variant<int>;
335 using V2 = std::variant<int, char *, long long>;
336 using V3 = std::variant<void *, int, int>;
337 constexpr V1 v1;
338 constexpr V2 v2(nullptr);
339 constexpr V3 v3;
340 static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
341 }
342 {
343 using V = std::variant<int, long, double, int *>;
344 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
345 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
346 }
347 {
348 using V = std::variant<int, long, double, long long, int *>;
349 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
350 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
351 }
352 }
353
test_exceptions()354 void test_exceptions() {
355 #ifndef TEST_HAS_NO_EXCEPTIONS
356 ReturnArity obj{};
357 auto test = [&](auto &&... args) {
358 try {
359 std::visit(obj, args...);
360 } catch (const std::bad_variant_access &) {
361 return true;
362 } catch (...) {
363 }
364 return false;
365 };
366 {
367 using V = std::variant<int, MakeEmptyT>;
368 V v;
369 makeEmpty(v);
370 assert(test(v));
371 }
372 {
373 using V = std::variant<int, MakeEmptyT>;
374 using V2 = std::variant<long, std::string, void *>;
375 V v;
376 makeEmpty(v);
377 V2 v2("hello");
378 assert(test(v, v2));
379 }
380 {
381 using V = std::variant<int, MakeEmptyT>;
382 using V2 = std::variant<long, std::string, void *>;
383 V v;
384 makeEmpty(v);
385 V2 v2("hello");
386 assert(test(v2, v));
387 }
388 {
389 using V = std::variant<int, MakeEmptyT>;
390 using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
391 V v;
392 makeEmpty(v);
393 V2 v2;
394 makeEmpty(v2);
395 assert(test(v, v2));
396 }
397 {
398 using V = std::variant<int, long, double, MakeEmptyT>;
399 V v1(42l), v2(101), v3(202), v4(1.1);
400 makeEmpty(v1);
401 assert(test(v1, v2, v3, v4));
402 }
403 {
404 using V = std::variant<int, long, double, long long, MakeEmptyT>;
405 V v1(42l), v2(101), v3(202), v4(1.1);
406 makeEmpty(v1);
407 makeEmpty(v2);
408 makeEmpty(v3);
409 makeEmpty(v4);
410 assert(test(v1, v2, v3, v4));
411 }
412 #endif
413 }
414
415 // See https://bugs.llvm.org/show_bug.cgi?id=31916
test_caller_accepts_nonconst()416 void test_caller_accepts_nonconst() {
417 struct A {};
418 struct Visitor {
419 void operator()(A&) {}
420 };
421 std::variant<A> v;
422 std::visit(Visitor{}, v);
423 }
424
main(int,char **)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
433 return 0;
434 }
435