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> bool operator()(Args &&...) & { 49 set_call<Args &&...>(CT_NonConst | CT_LValue); 50 return true; 51 } 52 53 template <class... Args> bool operator()(Args &&...) const & { 54 set_call<Args &&...>(CT_Const | CT_LValue); 55 return true; 56 } 57 58 // Don't allow the call operator to be invoked as an rvalue. 59 template <class... Args> bool operator()(Args &&...) && { 60 set_call<Args &&...>(CT_NonConst | CT_RValue); 61 return true; 62 } 63 64 template <class... Args> bool operator()(Args &&...) const && { 65 set_call<Args &&...>(CT_Const | CT_RValue); 66 return true; 67 } 68 69 template <class... Args> static void set_call(CallType type) { 70 assert(last_call_type == CT_None); 71 assert(last_call_args == nullptr); 72 last_call_type = type; 73 last_call_args = std::addressof(makeArgumentID<Args...>()); 74 } 75 76 template <class... Args> static bool check_call(CallType type) { 77 bool result = last_call_type == type && last_call_args && 78 *last_call_args == makeArgumentID<Args...>(); 79 last_call_type = CT_None; 80 last_call_args = nullptr; 81 return result; 82 } 83 84 static CallType last_call_type; 85 static const TypeID *last_call_args; 86 }; 87 88 CallType ForwardingCallObject::last_call_type = CT_None; 89 const TypeID *ForwardingCallObject::last_call_args = nullptr; 90 91 void test_call_operator_forwarding() { 92 using Fn = ForwardingCallObject; 93 Fn obj{}; 94 const Fn &cobj = obj; 95 { // test call operator forwarding - no variant 96 std::visit(obj); 97 assert(Fn::check_call<>(CT_NonConst | CT_LValue)); 98 std::visit(cobj); 99 assert(Fn::check_call<>(CT_Const | CT_LValue)); 100 std::visit(std::move(obj)); 101 assert(Fn::check_call<>(CT_NonConst | CT_RValue)); 102 std::visit(std::move(cobj)); 103 assert(Fn::check_call<>(CT_Const | CT_RValue)); 104 } 105 { // test call operator forwarding - single variant, single arg 106 using V = std::variant<int>; 107 V v(42); 108 std::visit(obj, v); 109 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); 110 std::visit(cobj, v); 111 assert(Fn::check_call<int &>(CT_Const | CT_LValue)); 112 std::visit(std::move(obj), v); 113 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); 114 std::visit(std::move(cobj), v); 115 assert(Fn::check_call<int &>(CT_Const | CT_RValue)); 116 } 117 { // test call operator forwarding - single variant, multi arg 118 using V = std::variant<int, long, double>; 119 V v(42l); 120 std::visit(obj, v); 121 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); 122 std::visit(cobj, v); 123 assert(Fn::check_call<long &>(CT_Const | CT_LValue)); 124 std::visit(std::move(obj), v); 125 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); 126 std::visit(std::move(cobj), v); 127 assert(Fn::check_call<long &>(CT_Const | CT_RValue)); 128 } 129 { // test call operator forwarding - multi variant, multi arg 130 using V = std::variant<int, long, double>; 131 using V2 = std::variant<int *, std::string>; 132 V v(42l); 133 V2 v2("hello"); 134 std::visit(obj, v, v2); 135 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); 136 std::visit(cobj, v, v2); 137 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); 138 std::visit(std::move(obj), v, v2); 139 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); 140 std::visit(std::move(cobj), v, v2); 141 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); 142 } 143 { 144 using V = std::variant<int, long, double, std::string>; 145 V v1(42l), v2("hello"), v3(101), v4(1.1); 146 std::visit(obj, v1, v2, v3, v4); 147 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue))); 148 std::visit(cobj, v1, v2, v3, v4); 149 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue))); 150 std::visit(std::move(obj), v1, v2, v3, v4); 151 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue))); 152 std::visit(std::move(cobj), v1, v2, v3, v4); 153 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue))); 154 } 155 { 156 using V = std::variant<int, long, double, int*, std::string>; 157 V v1(42l), v2("hello"), v3(nullptr), v4(1.1); 158 std::visit(obj, v1, v2, v3, v4); 159 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue))); 160 std::visit(cobj, v1, v2, v3, v4); 161 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue))); 162 std::visit(std::move(obj), v1, v2, v3, v4); 163 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue))); 164 std::visit(std::move(cobj), v1, v2, v3, v4); 165 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue))); 166 } 167 } 168 169 void test_argument_forwarding() { 170 using Fn = ForwardingCallObject; 171 Fn obj{}; 172 const auto Val = CT_LValue | CT_NonConst; 173 { // single argument - value type 174 using V = std::variant<int>; 175 V v(42); 176 const V &cv = v; 177 std::visit(obj, v); 178 assert(Fn::check_call<int &>(Val)); 179 std::visit(obj, cv); 180 assert(Fn::check_call<const int &>(Val)); 181 std::visit(obj, std::move(v)); 182 assert(Fn::check_call<int &&>(Val)); 183 std::visit(obj, std::move(cv)); 184 assert(Fn::check_call<const int &&>(Val)); 185 } 186 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 187 { // single argument - lvalue reference 188 using V = std::variant<int &>; 189 int x = 42; 190 V v(x); 191 const V &cv = v; 192 std::visit(obj, v); 193 assert(Fn::check_call<int &>(Val)); 194 std::visit(obj, cv); 195 assert(Fn::check_call<int &>(Val)); 196 std::visit(obj, std::move(v)); 197 assert(Fn::check_call<int &>(Val)); 198 std::visit(obj, std::move(cv)); 199 assert(Fn::check_call<int &>(Val)); 200 } 201 { // single argument - rvalue reference 202 using V = std::variant<int &&>; 203 int x = 42; 204 V v(std::move(x)); 205 const V &cv = v; 206 std::visit(obj, v); 207 assert(Fn::check_call<int &>(Val)); 208 std::visit(obj, cv); 209 assert(Fn::check_call<int &>(Val)); 210 std::visit(obj, std::move(v)); 211 assert(Fn::check_call<int &&>(Val)); 212 std::visit(obj, std::move(cv)); 213 assert(Fn::check_call<int &&>(Val)); 214 } 215 #endif 216 { // multi argument - multi variant 217 using V = std::variant<int, std::string, long>; 218 V v1(42), v2("hello"), v3(43l); 219 std::visit(obj, v1, v2, v3); 220 assert((Fn::check_call<int &, std::string &, long &>(Val))); 221 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3)); 222 assert((Fn::check_call<const int &, const std::string &, long &&>(Val))); 223 } 224 { 225 using V = std::variant<int, long, double, std::string>; 226 V v1(42l), v2("hello"), v3(101), v4(1.1); 227 std::visit(obj, v1, v2, v3, v4); 228 assert((Fn::check_call<long &, std::string &, int &, double &>(Val))); 229 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); 230 assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val))); 231 } 232 { 233 using V = std::variant<int, long, double, int*, std::string>; 234 V v1(42l), v2("hello"), v3(nullptr), v4(1.1); 235 std::visit(obj, v1, v2, v3, v4); 236 assert((Fn::check_call<long &, std::string &, int *&, double &>(Val))); 237 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); 238 assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val))); 239 } 240 } 241 242 struct ReturnFirst { 243 template <class... Args> constexpr int operator()(int f, Args &&...) const { 244 return f; 245 } 246 }; 247 248 struct ReturnArity { 249 template <class... Args> constexpr int operator()(Args &&...) const { 250 return sizeof...(Args); 251 } 252 }; 253 254 void test_constexpr() { 255 constexpr ReturnFirst obj{}; 256 constexpr ReturnArity aobj{}; 257 { 258 using V = std::variant<int>; 259 constexpr V v(42); 260 static_assert(std::visit(obj, v) == 42, ""); 261 } 262 { 263 using V = std::variant<short, long, char>; 264 constexpr V v(42l); 265 static_assert(std::visit(obj, v) == 42, ""); 266 } 267 { 268 using V1 = std::variant<int>; 269 using V2 = std::variant<int, char *, long long>; 270 using V3 = std::variant<bool, int, int>; 271 constexpr V1 v1; 272 constexpr V2 v2(nullptr); 273 constexpr V3 v3; 274 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 275 } 276 { 277 using V1 = std::variant<int>; 278 using V2 = std::variant<int, char *, long long>; 279 using V3 = std::variant<void *, int, int>; 280 constexpr V1 v1; 281 constexpr V2 v2(nullptr); 282 constexpr V3 v3; 283 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 284 } 285 { 286 using V = std::variant<int, long, double, int *>; 287 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); 288 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); 289 } 290 { 291 using V = std::variant<int, long, double, long long, int *>; 292 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); 293 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); 294 } 295 } 296 297 void test_exceptions() { 298 #ifndef TEST_HAS_NO_EXCEPTIONS 299 ReturnArity obj{}; 300 auto test = [&](auto &&... args) { 301 try { 302 std::visit(obj, args...); 303 } catch (const std::bad_variant_access &) { 304 return true; 305 } catch (...) { 306 } 307 return false; 308 }; 309 { 310 using V = std::variant<int, MakeEmptyT>; 311 V v; 312 makeEmpty(v); 313 assert(test(v)); 314 } 315 { 316 using V = std::variant<int, MakeEmptyT>; 317 using V2 = std::variant<long, std::string, void *>; 318 V v; 319 makeEmpty(v); 320 V2 v2("hello"); 321 assert(test(v, v2)); 322 } 323 { 324 using V = std::variant<int, MakeEmptyT>; 325 using V2 = std::variant<long, std::string, void *>; 326 V v; 327 makeEmpty(v); 328 V2 v2("hello"); 329 assert(test(v2, v)); 330 } 331 { 332 using V = std::variant<int, MakeEmptyT>; 333 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 334 V v; 335 makeEmpty(v); 336 V2 v2; 337 makeEmpty(v2); 338 assert(test(v, v2)); 339 } 340 { 341 using V = std::variant<int, long, double, MakeEmptyT>; 342 V v1(42l), v2(101), v3(202), v4(1.1); 343 makeEmpty(v1); 344 assert(test(v1, v2, v3, v4)); 345 } 346 { 347 using V = std::variant<int, long, double, long long, MakeEmptyT>; 348 V v1(42l), v2(101), v3(202), v4(1.1); 349 makeEmpty(v1); 350 makeEmpty(v2); 351 makeEmpty(v3); 352 makeEmpty(v4); 353 assert(test(v1, v2, v3, v4)); 354 } 355 #endif 356 } 357 358 // See https://bugs.llvm.org/show_bug.cgi?id=31916 359 void test_caller_accepts_nonconst() { 360 struct A {}; 361 struct Visitor { 362 void operator()(A&) {} 363 }; 364 std::variant<A> v; 365 std::visit(Visitor{}, v); 366 } 367 368 int main(int, char**) { 369 test_call_operator_forwarding(); 370 test_argument_forwarding(); 371 test_constexpr(); 372 test_exceptions(); 373 test_caller_accepts_nonconst(); 374 375 return 0; 376 } 377