1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 // UNSUPPORTED: c++03, c++11, c++14 11 12 // Throwing bad_variant_access is supported starting in macosx10.13 13 // XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions 14 // XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions 15 // XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions 16 // XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions 17 18 // <variant> 19 // template <class Visitor, class... Variants> 20 // constexpr see below visit(Visitor&& vis, Variants&&... vars); 21 22 #include <cassert> 23 #include <memory> 24 #include <string> 25 #include <type_traits> 26 #include <utility> 27 #include <variant> 28 29 #include "test_macros.h" 30 #include "type_id.h" 31 #include "variant_test_helpers.h" 32 33 enum CallType : unsigned { 34 CT_None, 35 CT_NonConst = 1, 36 CT_Const = 2, 37 CT_LValue = 4, 38 CT_RValue = 8 39 }; 40 41 inline constexpr CallType operator|(CallType LHS, CallType RHS) { 42 return static_cast<CallType>(static_cast<unsigned>(LHS) | 43 static_cast<unsigned>(RHS)); 44 } 45 46 struct ForwardingCallObject { 47 48 template <class... Args> 49 ForwardingCallObject& operator()(Args&&...) & { 50 set_call<Args &&...>(CT_NonConst | CT_LValue); 51 return *this; 52 } 53 54 template <class... Args> 55 const ForwardingCallObject& operator()(Args&&...) const & { 56 set_call<Args &&...>(CT_Const | CT_LValue); 57 return *this; 58 } 59 60 template <class... Args> 61 ForwardingCallObject&& operator()(Args&&...) && { 62 set_call<Args &&...>(CT_NonConst | CT_RValue); 63 return std::move(*this); 64 } 65 66 template <class... Args> 67 const ForwardingCallObject&& operator()(Args&&...) const && { 68 set_call<Args &&...>(CT_Const | CT_RValue); 69 return std::move(*this); 70 } 71 72 template <class... Args> static void set_call(CallType type) { 73 assert(last_call_type == CT_None); 74 assert(last_call_args == nullptr); 75 last_call_type = type; 76 last_call_args = std::addressof(makeArgumentID<Args...>()); 77 } 78 79 template <class... Args> static bool check_call(CallType type) { 80 bool result = last_call_type == type && last_call_args && 81 *last_call_args == makeArgumentID<Args...>(); 82 last_call_type = CT_None; 83 last_call_args = nullptr; 84 return result; 85 } 86 87 static CallType last_call_type; 88 static const TypeID *last_call_args; 89 }; 90 91 CallType ForwardingCallObject::last_call_type = CT_None; 92 const TypeID *ForwardingCallObject::last_call_args = nullptr; 93 94 void test_call_operator_forwarding() { 95 using Fn = ForwardingCallObject; 96 Fn obj{}; 97 const Fn &cobj = obj; 98 { // test call operator forwarding - no variant 99 std::visit(obj); 100 assert(Fn::check_call<>(CT_NonConst | CT_LValue)); 101 std::visit(cobj); 102 assert(Fn::check_call<>(CT_Const | CT_LValue)); 103 std::visit(std::move(obj)); 104 assert(Fn::check_call<>(CT_NonConst | CT_RValue)); 105 std::visit(std::move(cobj)); 106 assert(Fn::check_call<>(CT_Const | CT_RValue)); 107 } 108 { // test call operator forwarding - single variant, single arg 109 using V = std::variant<int>; 110 V v(42); 111 std::visit(obj, v); 112 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); 113 std::visit(cobj, v); 114 assert(Fn::check_call<int &>(CT_Const | CT_LValue)); 115 std::visit(std::move(obj), v); 116 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); 117 std::visit(std::move(cobj), v); 118 assert(Fn::check_call<int &>(CT_Const | CT_RValue)); 119 } 120 { // test call operator forwarding - single variant, multi arg 121 using V = std::variant<int, long, double>; 122 V v(42l); 123 std::visit(obj, v); 124 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); 125 std::visit(cobj, v); 126 assert(Fn::check_call<long &>(CT_Const | CT_LValue)); 127 std::visit(std::move(obj), v); 128 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); 129 std::visit(std::move(cobj), v); 130 assert(Fn::check_call<long &>(CT_Const | CT_RValue)); 131 } 132 { // test call operator forwarding - multi variant, multi arg 133 using V = std::variant<int, long, double>; 134 using V2 = std::variant<int *, std::string>; 135 V v(42l); 136 V2 v2("hello"); 137 std::visit(obj, v, v2); 138 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); 139 std::visit(cobj, v, v2); 140 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); 141 std::visit(std::move(obj), v, v2); 142 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); 143 std::visit(std::move(cobj), v, v2); 144 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); 145 } 146 { 147 using V = std::variant<int, long, double, std::string>; 148 V v1(42l), v2("hello"), v3(101), v4(1.1); 149 std::visit(obj, v1, v2, v3, v4); 150 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue))); 151 std::visit(cobj, v1, v2, v3, v4); 152 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue))); 153 std::visit(std::move(obj), v1, v2, v3, v4); 154 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue))); 155 std::visit(std::move(cobj), v1, v2, v3, v4); 156 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue))); 157 } 158 { 159 using V = std::variant<int, long, double, int*, std::string>; 160 V v1(42l), v2("hello"), v3(nullptr), v4(1.1); 161 std::visit(obj, v1, v2, v3, v4); 162 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue))); 163 std::visit(cobj, v1, v2, v3, v4); 164 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue))); 165 std::visit(std::move(obj), v1, v2, v3, v4); 166 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue))); 167 std::visit(std::move(cobj), v1, v2, v3, v4); 168 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue))); 169 } 170 } 171 172 void test_argument_forwarding() { 173 using Fn = ForwardingCallObject; 174 Fn obj{}; 175 const auto Val = CT_LValue | CT_NonConst; 176 { // single argument - value type 177 using V = std::variant<int>; 178 V v(42); 179 const V &cv = v; 180 std::visit(obj, v); 181 assert(Fn::check_call<int &>(Val)); 182 std::visit(obj, cv); 183 assert(Fn::check_call<const int &>(Val)); 184 std::visit(obj, std::move(v)); 185 assert(Fn::check_call<int &&>(Val)); 186 std::visit(obj, std::move(cv)); 187 assert(Fn::check_call<const int &&>(Val)); 188 } 189 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 190 { // single argument - lvalue reference 191 using V = std::variant<int &>; 192 int x = 42; 193 V v(x); 194 const V &cv = v; 195 std::visit(obj, v); 196 assert(Fn::check_call<int &>(Val)); 197 std::visit(obj, cv); 198 assert(Fn::check_call<int &>(Val)); 199 std::visit(obj, std::move(v)); 200 assert(Fn::check_call<int &>(Val)); 201 std::visit(obj, std::move(cv)); 202 assert(Fn::check_call<int &>(Val)); 203 } 204 { // single argument - rvalue reference 205 using V = std::variant<int &&>; 206 int x = 42; 207 V v(std::move(x)); 208 const V &cv = v; 209 std::visit(obj, v); 210 assert(Fn::check_call<int &>(Val)); 211 std::visit(obj, cv); 212 assert(Fn::check_call<int &>(Val)); 213 std::visit(obj, std::move(v)); 214 assert(Fn::check_call<int &&>(Val)); 215 std::visit(obj, std::move(cv)); 216 assert(Fn::check_call<int &&>(Val)); 217 } 218 #endif 219 { // multi argument - multi variant 220 using V = std::variant<int, std::string, long>; 221 V v1(42), v2("hello"), v3(43l); 222 std::visit(obj, v1, v2, v3); 223 assert((Fn::check_call<int &, std::string &, long &>(Val))); 224 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3)); 225 assert((Fn::check_call<const int &, const std::string &, long &&>(Val))); 226 } 227 { 228 using V = std::variant<int, long, double, std::string>; 229 V v1(42l), v2("hello"), v3(101), v4(1.1); 230 std::visit(obj, v1, v2, v3, v4); 231 assert((Fn::check_call<long &, std::string &, int &, double &>(Val))); 232 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); 233 assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val))); 234 } 235 { 236 using V = std::variant<int, long, double, int*, std::string>; 237 V v1(42l), v2("hello"), v3(nullptr), v4(1.1); 238 std::visit(obj, v1, v2, v3, v4); 239 assert((Fn::check_call<long &, std::string &, int *&, double &>(Val))); 240 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); 241 assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val))); 242 } 243 } 244 245 void test_return_type() { 246 using Fn = ForwardingCallObject; 247 Fn obj{}; 248 const Fn &cobj = obj; 249 { // test call operator forwarding - no variant 250 static_assert(std::is_same_v<decltype(std::visit(obj)), Fn&>); 251 static_assert(std::is_same_v<decltype(std::visit(cobj)), const Fn&>); 252 static_assert(std::is_same_v<decltype(std::visit(std::move(obj))), Fn&&>); 253 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj))), const Fn&&>); 254 } 255 { // test call operator forwarding - single variant, single arg 256 using V = std::variant<int>; 257 V v(42); 258 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>); 259 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>); 260 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>); 261 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>); 262 } 263 { // test call operator forwarding - single variant, multi arg 264 using V = std::variant<int, long, double>; 265 V v(42l); 266 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>); 267 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>); 268 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>); 269 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>); 270 } 271 { // test call operator forwarding - multi variant, multi arg 272 using V = std::variant<int, long, double>; 273 using V2 = std::variant<int *, std::string>; 274 V v(42l); 275 V2 v2("hello"); 276 static_assert(std::is_same_v<decltype(std::visit(obj, v, v2)), Fn&>); 277 static_assert(std::is_same_v<decltype(std::visit(cobj, v, v2)), const Fn&>); 278 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v, v2)), Fn&&>); 279 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v, v2)), const Fn&&>); 280 } 281 { 282 using V = std::variant<int, long, double, std::string>; 283 V v1(42l), v2("hello"), v3(101), v4(1.1); 284 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>); 285 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>); 286 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>); 287 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>); 288 } 289 { 290 using V = std::variant<int, long, double, int*, std::string>; 291 V v1(42l), v2("hello"), v3(nullptr), v4(1.1); 292 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>); 293 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>); 294 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>); 295 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>); 296 } 297 } 298 299 struct ReturnFirst { 300 template <class... Args> constexpr int operator()(int f, Args &&...) const { 301 return f; 302 } 303 }; 304 305 struct ReturnArity { 306 template <class... Args> constexpr int operator()(Args &&...) const { 307 return sizeof...(Args); 308 } 309 }; 310 311 void test_constexpr() { 312 constexpr ReturnFirst obj{}; 313 constexpr ReturnArity aobj{}; 314 { 315 using V = std::variant<int>; 316 constexpr V v(42); 317 static_assert(std::visit(obj, v) == 42, ""); 318 } 319 { 320 using V = std::variant<short, long, char>; 321 constexpr V v(42l); 322 static_assert(std::visit(obj, v) == 42, ""); 323 } 324 { 325 using V1 = std::variant<int>; 326 using V2 = std::variant<int, char *, long long>; 327 using V3 = std::variant<bool, int, int>; 328 constexpr V1 v1; 329 constexpr V2 v2(nullptr); 330 constexpr V3 v3; 331 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 332 } 333 { 334 using V1 = std::variant<int>; 335 using V2 = std::variant<int, char *, long long>; 336 using V3 = std::variant<void *, int, int>; 337 constexpr V1 v1; 338 constexpr V2 v2(nullptr); 339 constexpr V3 v3; 340 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 341 } 342 { 343 using V = std::variant<int, long, double, int *>; 344 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); 345 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); 346 } 347 { 348 using V = std::variant<int, long, double, long long, int *>; 349 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); 350 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); 351 } 352 } 353 354 void test_exceptions() { 355 #ifndef TEST_HAS_NO_EXCEPTIONS 356 ReturnArity obj{}; 357 auto test = [&](auto &&... args) { 358 try { 359 std::visit(obj, args...); 360 } catch (const std::bad_variant_access &) { 361 return true; 362 } catch (...) { 363 } 364 return false; 365 }; 366 { 367 using V = std::variant<int, MakeEmptyT>; 368 V v; 369 makeEmpty(v); 370 assert(test(v)); 371 } 372 { 373 using V = std::variant<int, MakeEmptyT>; 374 using V2 = std::variant<long, std::string, void *>; 375 V v; 376 makeEmpty(v); 377 V2 v2("hello"); 378 assert(test(v, v2)); 379 } 380 { 381 using V = std::variant<int, MakeEmptyT>; 382 using V2 = std::variant<long, std::string, void *>; 383 V v; 384 makeEmpty(v); 385 V2 v2("hello"); 386 assert(test(v2, v)); 387 } 388 { 389 using V = std::variant<int, MakeEmptyT>; 390 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 391 V v; 392 makeEmpty(v); 393 V2 v2; 394 makeEmpty(v2); 395 assert(test(v, v2)); 396 } 397 { 398 using V = std::variant<int, long, double, MakeEmptyT>; 399 V v1(42l), v2(101), v3(202), v4(1.1); 400 makeEmpty(v1); 401 assert(test(v1, v2, v3, v4)); 402 } 403 { 404 using V = std::variant<int, long, double, long long, MakeEmptyT>; 405 V v1(42l), v2(101), v3(202), v4(1.1); 406 makeEmpty(v1); 407 makeEmpty(v2); 408 makeEmpty(v3); 409 makeEmpty(v4); 410 assert(test(v1, v2, v3, v4)); 411 } 412 #endif 413 } 414 415 // See https://bugs.llvm.org/show_bug.cgi?id=31916 416 void test_caller_accepts_nonconst() { 417 struct A {}; 418 struct Visitor { 419 void operator()(A&) {} 420 }; 421 std::variant<A> v; 422 std::visit(Visitor{}, v); 423 } 424 425 int main(int, char**) { 426 test_call_operator_forwarding(); 427 test_argument_forwarding(); 428 test_return_type(); 429 test_constexpr(); 430 test_exceptions(); 431 test_caller_accepts_nonconst(); 432 433 return 0; 434 } 435