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 
145 void test_argument_forwarding() {
146   using Fn = ForwardingCallObject;
147   Fn obj{};
148   const auto Val = CT_LValue | CT_NonConst;
149   { // single argument - value type
150     using V = std::variant<int>;
151     V v(42);
152     const V &cv = v;
153     std::visit(obj, v);
154     assert(Fn::check_call<int &>(Val));
155     std::visit(obj, cv);
156     assert(Fn::check_call<const int &>(Val));
157     std::visit(obj, std::move(v));
158     assert(Fn::check_call<int &&>(Val));
159     std::visit(obj, std::move(cv));
160     assert(Fn::check_call<const int &&>(Val));
161   }
162 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
163   { // single argument - lvalue reference
164     using V = std::variant<int &>;
165     int x = 42;
166     V v(x);
167     const V &cv = v;
168     std::visit(obj, v);
169     assert(Fn::check_call<int &>(Val));
170     std::visit(obj, cv);
171     assert(Fn::check_call<int &>(Val));
172     std::visit(obj, std::move(v));
173     assert(Fn::check_call<int &>(Val));
174     std::visit(obj, std::move(cv));
175     assert(Fn::check_call<int &>(Val));
176   }
177   { // single argument - rvalue reference
178     using V = std::variant<int &&>;
179     int x = 42;
180     V v(std::move(x));
181     const V &cv = v;
182     std::visit(obj, v);
183     assert(Fn::check_call<int &>(Val));
184     std::visit(obj, cv);
185     assert(Fn::check_call<int &>(Val));
186     std::visit(obj, std::move(v));
187     assert(Fn::check_call<int &&>(Val));
188     std::visit(obj, std::move(cv));
189     assert(Fn::check_call<int &&>(Val));
190   }
191   { // multi argument - multi variant
192     using S = const std::string &;
193     using V = std::variant<int, S, long &&>;
194     const std::string str = "hello";
195     long l = 43;
196     V v1(42);
197     const V &cv1 = v1;
198     V v2(str);
199     const V &cv2 = v2;
200     V v3(std::move(l));
201     const V &cv3 = v3;
202     std::visit(obj, v1, v2, v3);
203     assert((Fn::check_call<int &, S, long &>(Val)));
204     std::visit(obj, cv1, cv2, std::move(v3));
205     assert((Fn::check_call<const int &, S, long &&>(Val)));
206   }
207 #endif
208 }
209 
210 struct ReturnFirst {
211   template <class... Args> constexpr int operator()(int f, Args &&...) const {
212     return f;
213   }
214 };
215 
216 struct ReturnArity {
217   template <class... Args> constexpr int operator()(Args &&...) const {
218     return sizeof...(Args);
219   }
220 };
221 
222 void test_constexpr() {
223   constexpr ReturnFirst obj{};
224   constexpr ReturnArity aobj{};
225   {
226     using V = std::variant<int>;
227     constexpr V v(42);
228     static_assert(std::visit(obj, v) == 42, "");
229   }
230   {
231     using V = std::variant<short, long, char>;
232     constexpr V v(42l);
233     static_assert(std::visit(obj, v) == 42, "");
234   }
235   {
236     using V1 = std::variant<int>;
237     using V2 = std::variant<int, char *, long long>;
238     using V3 = std::variant<bool, int, int>;
239     constexpr V1 v1;
240     constexpr V2 v2(nullptr);
241     constexpr V3 v3;
242     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
243   }
244   {
245     using V1 = std::variant<int>;
246     using V2 = std::variant<int, char *, long long>;
247     using V3 = std::variant<void *, int, int>;
248     constexpr V1 v1;
249     constexpr V2 v2(nullptr);
250     constexpr V3 v3;
251     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
252   }
253 }
254 
255 void test_exceptions() {
256 #ifndef TEST_HAS_NO_EXCEPTIONS
257   ReturnArity obj{};
258   auto test = [&](auto &&... args) {
259     try {
260       std::visit(obj, args...);
261     } catch (const std::bad_variant_access &) {
262       return true;
263     } catch (...) {
264     }
265     return false;
266   };
267   {
268     using V = std::variant<int, MakeEmptyT>;
269     V v;
270     makeEmpty(v);
271     assert(test(v));
272   }
273   {
274     using V = std::variant<int, MakeEmptyT>;
275     using V2 = std::variant<long, std::string, void *>;
276     V v;
277     makeEmpty(v);
278     V2 v2("hello");
279     assert(test(v, v2));
280   }
281   {
282     using V = std::variant<int, MakeEmptyT>;
283     using V2 = std::variant<long, std::string, void *>;
284     V v;
285     makeEmpty(v);
286     V2 v2("hello");
287     assert(test(v2, v));
288   }
289   {
290     using V = std::variant<int, MakeEmptyT>;
291     using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
292     V v;
293     makeEmpty(v);
294     V2 v2;
295     makeEmpty(v2);
296     assert(test(v, v2));
297   }
298 #endif
299 }
300 
301 // See https://bugs.llvm.org/show_bug.cgi?id=31916
302 void test_caller_accepts_nonconst() {
303   struct A {};
304   struct Visitor {
305     void operator()(A&) {}
306   };
307   std::variant<A> v;
308   std::visit(Visitor{}, v);
309 }
310 
311 int main(int, char**) {
312   test_call_operator_forwarding();
313   test_argument_forwarding();
314   test_constexpr();
315   test_exceptions();
316   test_caller_accepts_nonconst();
317 
318   return 0;
319 }
320