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