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++98, c++03, c++11, c++14
11 
12 // XFAIL: dylib-has-no-bad_variant_access && !no-exceptions
13 
14 // <variant>
15 // template <class Visitor, class... Variants>
16 // constexpr see below visit(Visitor&& vis, Variants&&... vars);
17 
18 #include <cassert>
19 #include <memory>
20 #include <string>
21 #include <type_traits>
22 #include <utility>
23 #include <variant>
24 
25 #include "test_macros.h"
26 #include "type_id.h"
27 #include "variant_test_helpers.h"
28 
29 enum CallType : unsigned {
30   CT_None,
31   CT_NonConst = 1,
32   CT_Const = 2,
33   CT_LValue = 4,
34   CT_RValue = 8
35 };
36 
37 inline constexpr CallType operator|(CallType LHS, CallType RHS) {
38   return static_cast<CallType>(static_cast<unsigned>(LHS) |
39                                static_cast<unsigned>(RHS));
40 }
41 
42 struct ForwardingCallObject {
43 
44   template <class... Args> bool operator()(Args &&...) & {
45     set_call<Args &&...>(CT_NonConst | CT_LValue);
46     return true;
47   }
48 
49   template <class... Args> bool operator()(Args &&...) const & {
50     set_call<Args &&...>(CT_Const | CT_LValue);
51     return true;
52   }
53 
54   // Don't allow the call operator to be invoked as an rvalue.
55   template <class... Args> bool operator()(Args &&...) && {
56     set_call<Args &&...>(CT_NonConst | CT_RValue);
57     return true;
58   }
59 
60   template <class... Args> bool operator()(Args &&...) const && {
61     set_call<Args &&...>(CT_Const | CT_RValue);
62     return true;
63   }
64 
65   template <class... Args> static void set_call(CallType type) {
66     assert(last_call_type == CT_None);
67     assert(last_call_args == nullptr);
68     last_call_type = type;
69     last_call_args = std::addressof(makeArgumentID<Args...>());
70   }
71 
72   template <class... Args> static bool check_call(CallType type) {
73     bool result = last_call_type == type && last_call_args &&
74                   *last_call_args == makeArgumentID<Args...>();
75     last_call_type = CT_None;
76     last_call_args = nullptr;
77     return result;
78   }
79 
80   static CallType last_call_type;
81   static const TypeID *last_call_args;
82 };
83 
84 CallType ForwardingCallObject::last_call_type = CT_None;
85 const TypeID *ForwardingCallObject::last_call_args = nullptr;
86 
87 void test_call_operator_forwarding() {
88   using Fn = ForwardingCallObject;
89   Fn obj{};
90   const Fn &cobj = obj;
91   { // test call operator forwarding - no variant
92     std::visit(obj);
93     assert(Fn::check_call<>(CT_NonConst | CT_LValue));
94     std::visit(cobj);
95     assert(Fn::check_call<>(CT_Const | CT_LValue));
96     std::visit(std::move(obj));
97     assert(Fn::check_call<>(CT_NonConst | CT_RValue));
98     std::visit(std::move(cobj));
99     assert(Fn::check_call<>(CT_Const | CT_RValue));
100   }
101   { // test call operator forwarding - single variant, single arg
102     using V = std::variant<int>;
103     V v(42);
104     std::visit(obj, v);
105     assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
106     std::visit(cobj, v);
107     assert(Fn::check_call<int &>(CT_Const | CT_LValue));
108     std::visit(std::move(obj), v);
109     assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
110     std::visit(std::move(cobj), v);
111     assert(Fn::check_call<int &>(CT_Const | CT_RValue));
112   }
113   { // test call operator forwarding - single variant, multi arg
114     using V = std::variant<int, long, double>;
115     V v(42l);
116     std::visit(obj, v);
117     assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
118     std::visit(cobj, v);
119     assert(Fn::check_call<long &>(CT_Const | CT_LValue));
120     std::visit(std::move(obj), v);
121     assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
122     std::visit(std::move(cobj), v);
123     assert(Fn::check_call<long &>(CT_Const | CT_RValue));
124   }
125   { // test call operator forwarding - multi variant, multi arg
126     using V = std::variant<int, long, double>;
127     using V2 = std::variant<int *, std::string>;
128     V v(42l);
129     V2 v2("hello");
130     std::visit(obj, v, v2);
131     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
132     std::visit(cobj, v, v2);
133     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
134     std::visit(std::move(obj), v, v2);
135     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
136     std::visit(std::move(cobj), v, v2);
137     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
138   }
139 }
140 
141 void test_argument_forwarding() {
142   using Fn = ForwardingCallObject;
143   Fn obj{};
144   const auto Val = CT_LValue | CT_NonConst;
145   { // single argument - value type
146     using V = std::variant<int>;
147     V v(42);
148     const V &cv = v;
149     std::visit(obj, v);
150     assert(Fn::check_call<int &>(Val));
151     std::visit(obj, cv);
152     assert(Fn::check_call<const int &>(Val));
153     std::visit(obj, std::move(v));
154     assert(Fn::check_call<int &&>(Val));
155     std::visit(obj, std::move(cv));
156     assert(Fn::check_call<const int &&>(Val));
157   }
158 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
159   { // single argument - lvalue reference
160     using V = std::variant<int &>;
161     int x = 42;
162     V v(x);
163     const V &cv = v;
164     std::visit(obj, v);
165     assert(Fn::check_call<int &>(Val));
166     std::visit(obj, cv);
167     assert(Fn::check_call<int &>(Val));
168     std::visit(obj, std::move(v));
169     assert(Fn::check_call<int &>(Val));
170     std::visit(obj, std::move(cv));
171     assert(Fn::check_call<int &>(Val));
172   }
173   { // single argument - rvalue reference
174     using V = std::variant<int &&>;
175     int x = 42;
176     V v(std::move(x));
177     const V &cv = v;
178     std::visit(obj, v);
179     assert(Fn::check_call<int &>(Val));
180     std::visit(obj, cv);
181     assert(Fn::check_call<int &>(Val));
182     std::visit(obj, std::move(v));
183     assert(Fn::check_call<int &&>(Val));
184     std::visit(obj, std::move(cv));
185     assert(Fn::check_call<int &&>(Val));
186   }
187   { // multi argument - multi variant
188     using S = const std::string &;
189     using V = std::variant<int, S, long &&>;
190     const std::string str = "hello";
191     long l = 43;
192     V v1(42);
193     const V &cv1 = v1;
194     V v2(str);
195     const V &cv2 = v2;
196     V v3(std::move(l));
197     const V &cv3 = v3;
198     std::visit(obj, v1, v2, v3);
199     assert((Fn::check_call<int &, S, long &>(Val)));
200     std::visit(obj, cv1, cv2, std::move(v3));
201     assert((Fn::check_call<const int &, S, long &&>(Val)));
202   }
203 #endif
204 }
205 
206 struct ReturnFirst {
207   template <class... Args> constexpr int operator()(int f, Args &&...) const {
208     return f;
209   }
210 };
211 
212 struct ReturnArity {
213   template <class... Args> constexpr int operator()(Args &&...) const {
214     return sizeof...(Args);
215   }
216 };
217 
218 void test_constexpr() {
219   constexpr ReturnFirst obj{};
220   constexpr ReturnArity aobj{};
221   {
222     using V = std::variant<int>;
223     constexpr V v(42);
224     static_assert(std::visit(obj, v) == 42, "");
225   }
226   {
227     using V = std::variant<short, long, char>;
228     constexpr V v(42l);
229     static_assert(std::visit(obj, v) == 42, "");
230   }
231   {
232     using V1 = std::variant<int>;
233     using V2 = std::variant<int, char *, long long>;
234     using V3 = std::variant<bool, int, int>;
235     constexpr V1 v1;
236     constexpr V2 v2(nullptr);
237     constexpr V3 v3;
238     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
239   }
240   {
241     using V1 = std::variant<int>;
242     using V2 = std::variant<int, char *, long long>;
243     using V3 = std::variant<void *, int, int>;
244     constexpr V1 v1;
245     constexpr V2 v2(nullptr);
246     constexpr V3 v3;
247     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
248   }
249 }
250 
251 void test_exceptions() {
252 #ifndef TEST_HAS_NO_EXCEPTIONS
253   ReturnArity obj{};
254   auto test = [&](auto &&... args) {
255     try {
256       std::visit(obj, args...);
257     } catch (const std::bad_variant_access &) {
258       return true;
259     } catch (...) {
260     }
261     return false;
262   };
263   {
264     using V = std::variant<int, MakeEmptyT>;
265     V v;
266     makeEmpty(v);
267     assert(test(v));
268   }
269   {
270     using V = std::variant<int, MakeEmptyT>;
271     using V2 = std::variant<long, std::string, void *>;
272     V v;
273     makeEmpty(v);
274     V2 v2("hello");
275     assert(test(v, v2));
276   }
277   {
278     using V = std::variant<int, MakeEmptyT>;
279     using V2 = std::variant<long, std::string, void *>;
280     V v;
281     makeEmpty(v);
282     V2 v2("hello");
283     assert(test(v2, v));
284   }
285   {
286     using V = std::variant<int, MakeEmptyT>;
287     using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
288     V v;
289     makeEmpty(v);
290     V2 v2;
291     makeEmpty(v2);
292     assert(test(v, v2));
293   }
294 #endif
295 }
296 
297 // See https://bugs.llvm.org/show_bug.cgi?id=31916
298 void test_caller_accepts_nonconst() {
299   struct A {};
300   struct Visitor {
301     void operator()(A&) {}
302   };
303   std::variant<A> v;
304   std::visit(Visitor{}, v);
305 }
306 
307 int main(int, char**) {
308   test_call_operator_forwarding();
309   test_argument_forwarding();
310   test_constexpr();
311   test_exceptions();
312   test_caller_accepts_nonconst();
313 
314   return 0;
315 }
316