xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp (revision dfde6e89ecc10b1f1eebdb0e409ef1a084030a6c)
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 // UNSUPPORTED: clang-16 || clang-17
12 // XFAIL: apple-clang
13 
14 // <variant>
15 
16 // class variant;
17 
18 // template<class Self, class Visitor>
19 //   constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
20 
21 #include <cassert>
22 #include <memory>
23 #include <string>
24 #include <tuple>
25 #include <type_traits>
26 #include <utility>
27 #include <variant>
28 
29 #include "test_macros.h"
30 #include "variant_test_helpers.h"
31 
32 void test_call_operator_forwarding() {
33   using Fn = ForwardingCallObject;
34   Fn obj{};
35   const Fn& cobj = obj;
36 
37   { // test call operator forwarding - single variant, single arg
38     using V = std::variant<int>;
39     V v(42);
40 
41     v.visit(obj);
42     assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
43     v.visit(cobj);
44     assert(Fn::check_call<int&>(CT_Const | CT_LValue));
45     v.visit(std::move(obj));
46     assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
47     v.visit(std::move(cobj));
48     assert(Fn::check_call<int&>(CT_Const | CT_RValue));
49   }
50   { // test call operator forwarding - single variant, multi arg
51     using V = std::variant<int, long, double>;
52     V v(42L);
53 
54     v.visit(obj);
55     assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
56     v.visit(cobj);
57     assert(Fn::check_call<long&>(CT_Const | CT_LValue));
58     v.visit(std::move(obj));
59     assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
60     v.visit(std::move(cobj));
61     assert(Fn::check_call<long&>(CT_Const | CT_RValue));
62   }
63 }
64 
65 // Applies to non-member `std::visit` only.
66 void test_argument_forwarding() {
67   using Fn = ForwardingCallObject;
68   Fn obj{};
69   const auto val = CT_LValue | CT_NonConst;
70 
71   { // single argument - value type
72     using V = std::variant<int>;
73     V v(42);
74     const V& cv = v;
75 
76     v.visit(obj);
77     assert(Fn::check_call<int&>(val));
78     cv.visit(obj);
79     assert(Fn::check_call<const int&>(val));
80     std::move(v).visit(obj);
81     assert(Fn::check_call<int&&>(val));
82     std::move(cv).visit(obj);
83     assert(Fn::check_call<const int&&>(val));
84   }
85 }
86 
87 void test_return_type() {
88   using Fn = ForwardingCallObject;
89   Fn obj{};
90   const Fn& cobj = obj;
91 
92   { // test call operator forwarding - single variant, single arg
93     using V = std::variant<int>;
94     V v(42);
95 
96     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
97     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
98     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
99     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
100   }
101   { // test call operator forwarding - single variant, multi arg
102     using V = std::variant<int, long, double>;
103     V v(42L);
104 
105     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
106     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
107     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
108     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
109   }
110 }
111 
112 void test_constexpr() {
113   constexpr ReturnFirst obj{};
114 
115   {
116     using V = std::variant<int>;
117     constexpr V v(42);
118 
119     static_assert(v.visit(obj) == 42);
120   }
121   {
122     using V = std::variant<short, long, char>;
123     constexpr V v(42L);
124 
125     static_assert(v.visit(obj) == 42);
126   }
127 }
128 
129 void test_exceptions() {
130 #ifndef TEST_HAS_NO_EXCEPTIONS
131   ReturnArity obj{};
132 
133   auto test = [&](auto&& v) {
134     try {
135       v.visit(obj);
136     } catch (const std::bad_variant_access&) {
137       return true;
138     } catch (...) {
139     }
140     return false;
141   };
142 
143   {
144     using V = std::variant<int, MakeEmptyT>;
145     V v;
146     makeEmpty(v);
147 
148     assert(test(v));
149   }
150 #endif
151 }
152 
153 // See https://llvm.org/PR31916
154 void test_caller_accepts_nonconst() {
155   struct A {};
156   struct Visitor {
157     void operator()(A&) {}
158   };
159   std::variant<A> v;
160 
161   v.visit(Visitor{});
162 }
163 
164 struct MyVariant : std::variant<short, long, float> {};
165 
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