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 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 86 { // single argument - lvalue reference 87 using V = std::variant<int&>; 88 int x = 42; 89 V v(x); 90 const V& cv = v; 91 92 v.visit(obj); 93 assert(Fn::check_call<int&>(val)); 94 cv.visit(obj); 95 assert(Fn::check_call<int&>(val)); 96 std::move(v).visit(obj); 97 assert(Fn::check_call<int&>(val)); 98 std::move(cv).visit(obj); 99 assert(Fn::check_call<int&>(val)); 100 assert(false); 101 } 102 { // single argument - rvalue reference 103 using V = std::variant<int&&>; 104 int x = 42; 105 V v(std::move(x)); 106 const V& cv = v; 107 108 v.visit(obj); 109 assert(Fn::check_call<int&>(val)); 110 cvstd::visit(obj); 111 assert(Fn::check_call<int&>(val)); 112 std::move(v).visit(obj); 113 assert(Fn::check_call<int&&>(val)); 114 std::move(cv).visit(obj); 115 assert(Fn::check_call<int&&>(val)); 116 } 117 #endif 118 } 119 120 void test_return_type() { 121 using Fn = ForwardingCallObject; 122 Fn obj{}; 123 const Fn& cobj = obj; 124 125 { // test call operator forwarding - single variant, single arg 126 using V = std::variant<int>; 127 V v(42); 128 129 static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>); 130 static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>); 131 static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>); 132 static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>); 133 } 134 { // test call operator forwarding - single variant, multi arg 135 using V = std::variant<int, long, double>; 136 V v(42L); 137 138 static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>); 139 static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>); 140 static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>); 141 static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>); 142 } 143 } 144 145 void test_constexpr() { 146 constexpr ReturnFirst obj{}; 147 148 { 149 using V = std::variant<int>; 150 constexpr V v(42); 151 152 static_assert(v.visit(obj) == 42); 153 } 154 { 155 using V = std::variant<short, long, char>; 156 constexpr V v(42L); 157 158 static_assert(v.visit(obj) == 42); 159 } 160 } 161 162 void test_exceptions() { 163 #ifndef TEST_HAS_NO_EXCEPTIONS 164 ReturnArity obj{}; 165 166 auto test = [&](auto&& v) { 167 try { 168 v.visit(obj); 169 } catch (const std::bad_variant_access&) { 170 return true; 171 } catch (...) { 172 } 173 return false; 174 }; 175 176 { 177 using V = std::variant<int, MakeEmptyT>; 178 V v; 179 makeEmpty(v); 180 181 assert(test(v)); 182 } 183 #endif 184 } 185 186 // See https://llvm.org/PR31916 187 void test_caller_accepts_nonconst() { 188 struct A {}; 189 struct Visitor { 190 void operator()(A&) {} 191 }; 192 std::variant<A> v; 193 194 v.visit(Visitor{}); 195 } 196 197 struct MyVariant : std::variant<short, long, float> {}; 198 199 namespace std { 200 template <std::size_t Index> 201 void get(const MyVariant&) { 202 assert(false); 203 } 204 } // namespace std 205 206 void test_derived_from_variant() { 207 auto v1 = MyVariant{42}; 208 const auto cv1 = MyVariant{142}; 209 210 v1.visit([](auto x) { assert(x == 42); }); 211 cv1.visit([](auto x) { assert(x == 142); }); 212 MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); }); 213 std::move(v1).visit([](auto x) { assert(x == 42); }); 214 std::move(cv1).visit([](auto x) { assert(x == 142); }); 215 216 // Check that visit does not take index nor valueless_by_exception members from the base class. 217 struct EvilVariantBase { 218 int index; 219 char valueless_by_exception; 220 }; 221 222 struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase { 223 using std::variant<int, long, double>::variant; 224 }; 225 226 EvilVariant1{12}.visit([](auto x) { assert(x == 12); }); 227 EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); }); 228 229 // Check that visit unambiguously picks the variant, even if the other base has __impl member. 230 struct ImplVariantBase { 231 struct Callable { 232 bool operator()() const { 233 assert(false); 234 return false; 235 } 236 }; 237 238 Callable __impl; 239 }; 240 241 struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase { 242 using std::variant<int, long, double>::variant; 243 }; 244 245 EvilVariant2{12}.visit([](auto x) { assert(x == 12); }); 246 EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); }); 247 } 248 249 int main(int, char**) { 250 test_call_operator_forwarding(); 251 test_argument_forwarding(); 252 test_return_type(); 253 test_constexpr(); 254 test_exceptions(); 255 test_caller_accepts_nonconst(); 256 test_derived_from_variant(); 257 258 return 0; 259 } 260