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
28enum CallType : unsigned {
29  CT_None,
30  CT_NonConst = 1,
31  CT_Const = 2,
32  CT_LValue = 4,
33  CT_RValue = 8
34};
35
36inline constexpr CallType operator|(CallType LHS, CallType RHS) {
37  return static_cast<CallType>(static_cast<unsigned>(LHS) |
38                               static_cast<unsigned>(RHS));
39}
40
41struct ForwardingCallObject {
42
43  template <class... Args> bool operator()(Args &&...) & {
44    set_call<Args &&...>(CT_NonConst | CT_LValue);
45    return true;
46  }
47
48  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.
54  template <class... Args> bool operator()(Args &&...) && {
55    set_call<Args &&...>(CT_NonConst | CT_RValue);
56    return true;
57  }
58
59  template <class... Args> bool operator()(Args &&...) const && {
60    set_call<Args &&...>(CT_Const | CT_RValue);
61    return true;
62  }
63
64  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
71  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
83CallType ForwardingCallObject::last_call_type = CT_None;
84const TypeID *ForwardingCallObject::last_call_args = nullptr;
85
86void 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
130void 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
195struct ReturnFirst {
196  template <class... Args> constexpr int operator()(int f, Args &&...) const {
197    return f;
198  }
199};
200
201struct ReturnArity {
202  template <class... Args> constexpr int operator()(Args &&...) const {
203    return sizeof...(Args);
204  }
205};
206
207void 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
240void 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
287void 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
296int main() {
297  test_call_operator_forwarding();
298  test_argument_forwarding();
299  test_constexpr();
300  test_exceptions();
301  test_caller_accepts_nonconst();
302}
303