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
31void 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.
65void 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
86void 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
111void 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
128void 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
153void 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
163struct MyVariant : std::variant<short, long, float> {};
164
165// FIXME: This is UB according to [namespace.std]
166namespace std {
167template <std::size_t Index>
168void get(const MyVariant&) {
169 assert(false);
170}
171} // namespace std
172
173void 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
216int 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

source code of libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp