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 
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> bool operator()(Args &&...) & {
49     set_call<Args &&...>(CT_NonConst | CT_LValue);
50     return true;
51   }
52 
53   template <class... Args> bool operator()(Args &&...) const & {
54     set_call<Args &&...>(CT_Const | CT_LValue);
55     return true;
56   }
57 
58   // Don't allow the call operator to be invoked as an rvalue.
59   template <class... Args> bool operator()(Args &&...) && {
60     set_call<Args &&...>(CT_NonConst | CT_RValue);
61     return true;
62   }
63 
64   template <class... Args> bool operator()(Args &&...) const && {
65     set_call<Args &&...>(CT_Const | CT_RValue);
66     return true;
67   }
68 
69   template <class... Args> static void set_call(CallType type) {
70     assert(last_call_type == CT_None);
71     assert(last_call_args == nullptr);
72     last_call_type = type;
73     last_call_args = std::addressof(makeArgumentID<Args...>());
74   }
75 
76   template <class... Args> static bool check_call(CallType type) {
77     bool result = last_call_type == type && last_call_args &&
78                   *last_call_args == makeArgumentID<Args...>();
79     last_call_type = CT_None;
80     last_call_args = nullptr;
81     return result;
82   }
83 
84   static CallType last_call_type;
85   static const TypeID *last_call_args;
86 };
87 
88 CallType ForwardingCallObject::last_call_type = CT_None;
89 const TypeID *ForwardingCallObject::last_call_args = nullptr;
90 
91 void test_call_operator_forwarding() {
92   using Fn = ForwardingCallObject;
93   Fn obj{};
94   const Fn &cobj = obj;
95   { // test call operator forwarding - no variant
96     std::visit(obj);
97     assert(Fn::check_call<>(CT_NonConst | CT_LValue));
98     std::visit(cobj);
99     assert(Fn::check_call<>(CT_Const | CT_LValue));
100     std::visit(std::move(obj));
101     assert(Fn::check_call<>(CT_NonConst | CT_RValue));
102     std::visit(std::move(cobj));
103     assert(Fn::check_call<>(CT_Const | CT_RValue));
104   }
105   { // test call operator forwarding - single variant, single arg
106     using V = std::variant<int>;
107     V v(42);
108     std::visit(obj, v);
109     assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
110     std::visit(cobj, v);
111     assert(Fn::check_call<int &>(CT_Const | CT_LValue));
112     std::visit(std::move(obj), v);
113     assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
114     std::visit(std::move(cobj), v);
115     assert(Fn::check_call<int &>(CT_Const | CT_RValue));
116   }
117   { // test call operator forwarding - single variant, multi arg
118     using V = std::variant<int, long, double>;
119     V v(42l);
120     std::visit(obj, v);
121     assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
122     std::visit(cobj, v);
123     assert(Fn::check_call<long &>(CT_Const | CT_LValue));
124     std::visit(std::move(obj), v);
125     assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
126     std::visit(std::move(cobj), v);
127     assert(Fn::check_call<long &>(CT_Const | CT_RValue));
128   }
129   { // test call operator forwarding - multi variant, multi arg
130     using V = std::variant<int, long, double>;
131     using V2 = std::variant<int *, std::string>;
132     V v(42l);
133     V2 v2("hello");
134     std::visit(obj, v, v2);
135     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
136     std::visit(cobj, v, v2);
137     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
138     std::visit(std::move(obj), v, v2);
139     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
140     std::visit(std::move(cobj), v, v2);
141     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
142   }
143   {
144     using V = std::variant<int, long, double, std::string>;
145     V v1(42l), v2("hello"), v3(101), v4(1.1);
146     std::visit(obj, v1, v2, v3, v4);
147     assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue)));
148     std::visit(cobj, v1, v2, v3, v4);
149     assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue)));
150     std::visit(std::move(obj), v1, v2, v3, v4);
151     assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue)));
152     std::visit(std::move(cobj), v1, v2, v3, v4);
153     assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue)));
154   }
155   {
156     using V = std::variant<int, long, double, int*, std::string>;
157     V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
158     std::visit(obj, v1, v2, v3, v4);
159     assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue)));
160     std::visit(cobj, v1, v2, v3, v4);
161     assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue)));
162     std::visit(std::move(obj), v1, v2, v3, v4);
163     assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue)));
164     std::visit(std::move(cobj), v1, v2, v3, v4);
165     assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue)));
166   }
167 }
168 
169 void test_argument_forwarding() {
170   using Fn = ForwardingCallObject;
171   Fn obj{};
172   const auto Val = CT_LValue | CT_NonConst;
173   { // single argument - value type
174     using V = std::variant<int>;
175     V v(42);
176     const V &cv = v;
177     std::visit(obj, v);
178     assert(Fn::check_call<int &>(Val));
179     std::visit(obj, cv);
180     assert(Fn::check_call<const int &>(Val));
181     std::visit(obj, std::move(v));
182     assert(Fn::check_call<int &&>(Val));
183     std::visit(obj, std::move(cv));
184     assert(Fn::check_call<const int &&>(Val));
185   }
186 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
187   { // single argument - lvalue reference
188     using V = std::variant<int &>;
189     int x = 42;
190     V v(x);
191     const V &cv = v;
192     std::visit(obj, v);
193     assert(Fn::check_call<int &>(Val));
194     std::visit(obj, cv);
195     assert(Fn::check_call<int &>(Val));
196     std::visit(obj, std::move(v));
197     assert(Fn::check_call<int &>(Val));
198     std::visit(obj, std::move(cv));
199     assert(Fn::check_call<int &>(Val));
200   }
201   { // single argument - rvalue reference
202     using V = std::variant<int &&>;
203     int x = 42;
204     V v(std::move(x));
205     const V &cv = v;
206     std::visit(obj, v);
207     assert(Fn::check_call<int &>(Val));
208     std::visit(obj, cv);
209     assert(Fn::check_call<int &>(Val));
210     std::visit(obj, std::move(v));
211     assert(Fn::check_call<int &&>(Val));
212     std::visit(obj, std::move(cv));
213     assert(Fn::check_call<int &&>(Val));
214   }
215 #endif
216   { // multi argument - multi variant
217     using V = std::variant<int, std::string, long>;
218     V v1(42), v2("hello"), v3(43l);
219     std::visit(obj, v1, v2, v3);
220     assert((Fn::check_call<int &, std::string &, long &>(Val)));
221     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3));
222     assert((Fn::check_call<const int &, const std::string &, long &&>(Val)));
223   }
224   {
225     using V = std::variant<int, long, double, std::string>;
226     V v1(42l), v2("hello"), v3(101), v4(1.1);
227     std::visit(obj, v1, v2, v3, v4);
228     assert((Fn::check_call<long &, std::string &, int &, double &>(Val)));
229     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
230     assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val)));
231   }
232   {
233     using V = std::variant<int, long, double, int*, std::string>;
234     V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
235     std::visit(obj, v1, v2, v3, v4);
236     assert((Fn::check_call<long &, std::string &, int *&, double &>(Val)));
237     std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
238     assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val)));
239   }
240 }
241 
242 struct ReturnFirst {
243   template <class... Args> constexpr int operator()(int f, Args &&...) const {
244     return f;
245   }
246 };
247 
248 struct ReturnArity {
249   template <class... Args> constexpr int operator()(Args &&...) const {
250     return sizeof...(Args);
251   }
252 };
253 
254 void test_constexpr() {
255   constexpr ReturnFirst obj{};
256   constexpr ReturnArity aobj{};
257   {
258     using V = std::variant<int>;
259     constexpr V v(42);
260     static_assert(std::visit(obj, v) == 42, "");
261   }
262   {
263     using V = std::variant<short, long, char>;
264     constexpr V v(42l);
265     static_assert(std::visit(obj, v) == 42, "");
266   }
267   {
268     using V1 = std::variant<int>;
269     using V2 = std::variant<int, char *, long long>;
270     using V3 = std::variant<bool, int, int>;
271     constexpr V1 v1;
272     constexpr V2 v2(nullptr);
273     constexpr V3 v3;
274     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
275   }
276   {
277     using V1 = std::variant<int>;
278     using V2 = std::variant<int, char *, long long>;
279     using V3 = std::variant<void *, int, int>;
280     constexpr V1 v1;
281     constexpr V2 v2(nullptr);
282     constexpr V3 v3;
283     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
284   }
285   {
286     using V = std::variant<int, long, double, int *>;
287     constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
288     static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
289   }
290   {
291     using V = std::variant<int, long, double, long long, int *>;
292     constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
293     static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");
294   }
295 }
296 
297 void test_exceptions() {
298 #ifndef TEST_HAS_NO_EXCEPTIONS
299   ReturnArity obj{};
300   auto test = [&](auto &&... args) {
301     try {
302       std::visit(obj, args...);
303     } catch (const std::bad_variant_access &) {
304       return true;
305     } catch (...) {
306     }
307     return false;
308   };
309   {
310     using V = std::variant<int, MakeEmptyT>;
311     V v;
312     makeEmpty(v);
313     assert(test(v));
314   }
315   {
316     using V = std::variant<int, MakeEmptyT>;
317     using V2 = std::variant<long, std::string, void *>;
318     V v;
319     makeEmpty(v);
320     V2 v2("hello");
321     assert(test(v, v2));
322   }
323   {
324     using V = std::variant<int, MakeEmptyT>;
325     using V2 = std::variant<long, std::string, void *>;
326     V v;
327     makeEmpty(v);
328     V2 v2("hello");
329     assert(test(v2, v));
330   }
331   {
332     using V = std::variant<int, MakeEmptyT>;
333     using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
334     V v;
335     makeEmpty(v);
336     V2 v2;
337     makeEmpty(v2);
338     assert(test(v, v2));
339   }
340   {
341     using V = std::variant<int, long, double, MakeEmptyT>;
342     V v1(42l), v2(101), v3(202), v4(1.1);
343     makeEmpty(v1);
344     assert(test(v1, v2, v3, v4));
345   }
346   {
347     using V = std::variant<int, long, double, long long, MakeEmptyT>;
348     V v1(42l), v2(101), v3(202), v4(1.1);
349     makeEmpty(v1);
350     makeEmpty(v2);
351     makeEmpty(v3);
352     makeEmpty(v4);
353     assert(test(v1, v2, v3, v4));
354   }
355 #endif
356 }
357 
358 // See https://bugs.llvm.org/show_bug.cgi?id=31916
359 void test_caller_accepts_nonconst() {
360   struct A {};
361   struct Visitor {
362     void operator()(A&) {}
363   };
364   std::variant<A> v;
365   std::visit(Visitor{}, v);
366 }
367 
368 int main(int, char**) {
369   test_call_operator_forwarding();
370   test_argument_forwarding();
371   test_constexpr();
372   test_exceptions();
373   test_caller_accepts_nonconst();
374 
375   return 0;
376 }
377