xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp (revision 7f845cba2ccc2ab637b8e40fbafb9f83a2d67c70)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10 // The tested functionality needs deducing this.
11 // XFAIL: apple-clang
12 
13 // <variant>
14 
15 // class variant;
16 
17 // template<class Self, class Visitor>
18 //   constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
19 
20 #include <cassert>
21 #include <memory>
22 #include <string>
23 #include <tuple>
24 #include <type_traits>
25 #include <utility>
26 #include <variant>
27 
28 #include "test_macros.h"
29 #include "variant_test_helpers.h"
30 
31 void test_call_operator_forwarding() {
32   using Fn = ForwardingCallObject;
33   Fn obj{};
34   const Fn& cobj = obj;
35 
36   { // test call operator forwarding - single variant, single arg
37     using V = std::variant<int>;
38     V v(42);
39 
40     v.visit(obj);
41     assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
42     v.visit(cobj);
43     assert(Fn::check_call<int&>(CT_Const | CT_LValue));
44     v.visit(std::move(obj));
45     assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
46     v.visit(std::move(cobj));
47     assert(Fn::check_call<int&>(CT_Const | CT_RValue));
48   }
49   { // test call operator forwarding - single variant, multi arg
50     using V = std::variant<int, long, double>;
51     V v(42L);
52 
53     v.visit(obj);
54     assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
55     v.visit(cobj);
56     assert(Fn::check_call<long&>(CT_Const | CT_LValue));
57     v.visit(std::move(obj));
58     assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
59     v.visit(std::move(cobj));
60     assert(Fn::check_call<long&>(CT_Const | CT_RValue));
61   }
62 }
63 
64 // Applies to non-member `std::visit` only.
65 void test_argument_forwarding() {
66   using Fn = ForwardingCallObject;
67   Fn obj{};
68   const auto val = CT_LValue | CT_NonConst;
69 
70   { // single argument - value type
71     using V = std::variant<int>;
72     V v(42);
73     const V& cv = v;
74 
75     v.visit(obj);
76     assert(Fn::check_call<int&>(val));
77     cv.visit(obj);
78     assert(Fn::check_call<const int&>(val));
79     std::move(v).visit(obj);
80     assert(Fn::check_call<int&&>(val));
81     std::move(cv).visit(obj);
82     assert(Fn::check_call<const int&&>(val));
83   }
84 }
85 
86 void test_return_type() {
87   using Fn = ForwardingCallObject;
88   Fn obj{};
89   const Fn& cobj = obj;
90 
91   { // test call operator forwarding - single variant, single arg
92     using V = std::variant<int>;
93     V v(42);
94 
95     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
96     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
97     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
98     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
99   }
100   { // test call operator forwarding - single variant, multi arg
101     using V = std::variant<int, long, double>;
102     V v(42L);
103 
104     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
105     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
106     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
107     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
108   }
109 }
110 
111 void test_constexpr() {
112   constexpr ReturnFirst obj{};
113 
114   {
115     using V = std::variant<int>;
116     constexpr V v(42);
117 
118     static_assert(v.visit(obj) == 42);
119   }
120   {
121     using V = std::variant<short, long, char>;
122     constexpr V v(42L);
123 
124     static_assert(v.visit(obj) == 42);
125   }
126 }
127 
128 void test_exceptions() {
129 #ifndef TEST_HAS_NO_EXCEPTIONS
130   ReturnArity obj{};
131 
132   auto test = [&](auto&& v) {
133     try {
134       v.visit(obj);
135     } catch (const std::bad_variant_access&) {
136       return true;
137     } catch (...) {
138     }
139     return false;
140   };
141 
142   {
143     using V = std::variant<int, MakeEmptyT>;
144     V v;
145     makeEmpty(v);
146 
147     assert(test(v));
148   }
149 #endif
150 }
151 
152 // See https://llvm.org/PR31916
153 void test_caller_accepts_nonconst() {
154   struct A {};
155   struct Visitor {
156     void operator()(A&) {}
157   };
158   std::variant<A> v;
159 
160   v.visit(Visitor{});
161 }
162 
163 struct MyVariant : std::variant<short, long, float> {};
164 
165 // FIXME: This is UB according to [namespace.std]
166 namespace std {
167 template <std::size_t Index>
168 void get(const MyVariant&) {
169   assert(false);
170 }
171 } // namespace std
172 
173 void test_derived_from_variant() {
174   auto v1        = MyVariant{42};
175   const auto cv1 = MyVariant{142};
176 
177   v1.visit([](auto x) { assert(x == 42); });
178   cv1.visit([](auto x) { assert(x == 142); });
179   MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); });
180   std::move(v1).visit([](auto x) { assert(x == 42); });
181   std::move(cv1).visit([](auto x) { assert(x == 142); });
182 
183   // Check that visit does not take index nor valueless_by_exception members from the base class.
184   struct EvilVariantBase {
185     int index;
186     char valueless_by_exception;
187   };
188 
189   struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {
190     using std::variant<int, long, double>::variant;
191   };
192 
193   EvilVariant1{12}.visit([](auto x) { assert(x == 12); });
194   EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); });
195 
196   // Check that visit unambiguously picks the variant, even if the other base has __impl member.
197   struct ImplVariantBase {
198     struct Callable {
199       bool operator()() const {
200         assert(false);
201         return false;
202       }
203     };
204 
205     Callable __impl;
206   };
207 
208   struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
209     using std::variant<int, long, double>::variant;
210   };
211 
212   EvilVariant2{12}.visit([](auto x) { assert(x == 12); });
213   EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); });
214 }
215 
216 int main(int, char**) {
217   test_call_operator_forwarding();
218   test_argument_forwarding();
219   test_return_type();
220   test_constexpr();
221   test_exceptions();
222   test_caller_accepts_nonconst();
223   test_derived_from_variant();
224 
225   return 0;
226 }
227