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