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++98, c++03, c++11, c++14 11 12 // XFAIL: dylib-has-no-bad_variant_access && !no-exceptions 13 14 // <variant> 15 // template <class Visitor, class... Variants> 16 // constexpr see below visit(Visitor&& vis, Variants&&... vars); 17 18 #include <cassert> 19 #include <memory> 20 #include <string> 21 #include <type_traits> 22 #include <utility> 23 #include <variant> 24 25 #include "test_macros.h" 26 #include "type_id.h" 27 #include "variant_test_helpers.h" 28 29 enum CallType : unsigned { 30 CT_None, 31 CT_NonConst = 1, 32 CT_Const = 2, 33 CT_LValue = 4, 34 CT_RValue = 8 35 }; 36 37 inline constexpr CallType operator|(CallType LHS, CallType RHS) { 38 return static_cast<CallType>(static_cast<unsigned>(LHS) | 39 static_cast<unsigned>(RHS)); 40 } 41 42 struct ForwardingCallObject { 43 44 template <class... Args> bool operator()(Args &&...) & { 45 set_call<Args &&...>(CT_NonConst | CT_LValue); 46 return true; 47 } 48 49 template <class... Args> bool operator()(Args &&...) const & { 50 set_call<Args &&...>(CT_Const | CT_LValue); 51 return true; 52 } 53 54 // Don't allow the call operator to be invoked as an rvalue. 55 template <class... Args> bool operator()(Args &&...) && { 56 set_call<Args &&...>(CT_NonConst | CT_RValue); 57 return true; 58 } 59 60 template <class... Args> bool operator()(Args &&...) const && { 61 set_call<Args &&...>(CT_Const | CT_RValue); 62 return true; 63 } 64 65 template <class... Args> static void set_call(CallType type) { 66 assert(last_call_type == CT_None); 67 assert(last_call_args == nullptr); 68 last_call_type = type; 69 last_call_args = std::addressof(makeArgumentID<Args...>()); 70 } 71 72 template <class... Args> static bool check_call(CallType type) { 73 bool result = last_call_type == type && last_call_args && 74 *last_call_args == makeArgumentID<Args...>(); 75 last_call_type = CT_None; 76 last_call_args = nullptr; 77 return result; 78 } 79 80 static CallType last_call_type; 81 static const TypeID *last_call_args; 82 }; 83 84 CallType ForwardingCallObject::last_call_type = CT_None; 85 const TypeID *ForwardingCallObject::last_call_args = nullptr; 86 87 void test_call_operator_forwarding() { 88 using Fn = ForwardingCallObject; 89 Fn obj{}; 90 const Fn &cobj = obj; 91 { // test call operator forwarding - no variant 92 std::visit(obj); 93 assert(Fn::check_call<>(CT_NonConst | CT_LValue)); 94 std::visit(cobj); 95 assert(Fn::check_call<>(CT_Const | CT_LValue)); 96 std::visit(std::move(obj)); 97 assert(Fn::check_call<>(CT_NonConst | CT_RValue)); 98 std::visit(std::move(cobj)); 99 assert(Fn::check_call<>(CT_Const | CT_RValue)); 100 } 101 { // test call operator forwarding - single variant, single arg 102 using V = std::variant<int>; 103 V v(42); 104 std::visit(obj, v); 105 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); 106 std::visit(cobj, v); 107 assert(Fn::check_call<int &>(CT_Const | CT_LValue)); 108 std::visit(std::move(obj), v); 109 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); 110 std::visit(std::move(cobj), v); 111 assert(Fn::check_call<int &>(CT_Const | CT_RValue)); 112 } 113 { // test call operator forwarding - single variant, multi arg 114 using V = std::variant<int, long, double>; 115 V v(42l); 116 std::visit(obj, v); 117 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); 118 std::visit(cobj, v); 119 assert(Fn::check_call<long &>(CT_Const | CT_LValue)); 120 std::visit(std::move(obj), v); 121 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); 122 std::visit(std::move(cobj), v); 123 assert(Fn::check_call<long &>(CT_Const | CT_RValue)); 124 } 125 { // test call operator forwarding - multi variant, multi arg 126 using V = std::variant<int, long, double>; 127 using V2 = std::variant<int *, std::string>; 128 V v(42l); 129 V2 v2("hello"); 130 std::visit(obj, v, v2); 131 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); 132 std::visit(cobj, v, v2); 133 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); 134 std::visit(std::move(obj), v, v2); 135 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); 136 std::visit(std::move(cobj), v, v2); 137 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); 138 } 139 } 140 141 void test_argument_forwarding() { 142 using Fn = ForwardingCallObject; 143 Fn obj{}; 144 const auto Val = CT_LValue | CT_NonConst; 145 { // single argument - value type 146 using V = std::variant<int>; 147 V v(42); 148 const V &cv = v; 149 std::visit(obj, v); 150 assert(Fn::check_call<int &>(Val)); 151 std::visit(obj, cv); 152 assert(Fn::check_call<const int &>(Val)); 153 std::visit(obj, std::move(v)); 154 assert(Fn::check_call<int &&>(Val)); 155 std::visit(obj, std::move(cv)); 156 assert(Fn::check_call<const int &&>(Val)); 157 } 158 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 159 { // single argument - lvalue reference 160 using V = std::variant<int &>; 161 int x = 42; 162 V v(x); 163 const V &cv = v; 164 std::visit(obj, v); 165 assert(Fn::check_call<int &>(Val)); 166 std::visit(obj, cv); 167 assert(Fn::check_call<int &>(Val)); 168 std::visit(obj, std::move(v)); 169 assert(Fn::check_call<int &>(Val)); 170 std::visit(obj, std::move(cv)); 171 assert(Fn::check_call<int &>(Val)); 172 } 173 { // single argument - rvalue reference 174 using V = std::variant<int &&>; 175 int x = 42; 176 V v(std::move(x)); 177 const V &cv = v; 178 std::visit(obj, v); 179 assert(Fn::check_call<int &>(Val)); 180 std::visit(obj, cv); 181 assert(Fn::check_call<int &>(Val)); 182 std::visit(obj, std::move(v)); 183 assert(Fn::check_call<int &&>(Val)); 184 std::visit(obj, std::move(cv)); 185 assert(Fn::check_call<int &&>(Val)); 186 } 187 { // multi argument - multi variant 188 using S = const std::string &; 189 using V = std::variant<int, S, long &&>; 190 const std::string str = "hello"; 191 long l = 43; 192 V v1(42); 193 const V &cv1 = v1; 194 V v2(str); 195 const V &cv2 = v2; 196 V v3(std::move(l)); 197 const V &cv3 = v3; 198 std::visit(obj, v1, v2, v3); 199 assert((Fn::check_call<int &, S, long &>(Val))); 200 std::visit(obj, cv1, cv2, std::move(v3)); 201 assert((Fn::check_call<const int &, S, long &&>(Val))); 202 } 203 #endif 204 } 205 206 struct ReturnFirst { 207 template <class... Args> constexpr int operator()(int f, Args &&...) const { 208 return f; 209 } 210 }; 211 212 struct ReturnArity { 213 template <class... Args> constexpr int operator()(Args &&...) const { 214 return sizeof...(Args); 215 } 216 }; 217 218 void test_constexpr() { 219 constexpr ReturnFirst obj{}; 220 constexpr ReturnArity aobj{}; 221 { 222 using V = std::variant<int>; 223 constexpr V v(42); 224 static_assert(std::visit(obj, v) == 42, ""); 225 } 226 { 227 using V = std::variant<short, long, char>; 228 constexpr V v(42l); 229 static_assert(std::visit(obj, v) == 42, ""); 230 } 231 { 232 using V1 = std::variant<int>; 233 using V2 = std::variant<int, char *, long long>; 234 using V3 = std::variant<bool, int, int>; 235 constexpr V1 v1; 236 constexpr V2 v2(nullptr); 237 constexpr V3 v3; 238 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 239 } 240 { 241 using V1 = std::variant<int>; 242 using V2 = std::variant<int, char *, long long>; 243 using V3 = std::variant<void *, int, int>; 244 constexpr V1 v1; 245 constexpr V2 v2(nullptr); 246 constexpr V3 v3; 247 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 248 } 249 } 250 251 void test_exceptions() { 252 #ifndef TEST_HAS_NO_EXCEPTIONS 253 ReturnArity obj{}; 254 auto test = [&](auto &&... args) { 255 try { 256 std::visit(obj, args...); 257 } catch (const std::bad_variant_access &) { 258 return true; 259 } catch (...) { 260 } 261 return false; 262 }; 263 { 264 using V = std::variant<int, MakeEmptyT>; 265 V v; 266 makeEmpty(v); 267 assert(test(v)); 268 } 269 { 270 using V = std::variant<int, MakeEmptyT>; 271 using V2 = std::variant<long, std::string, void *>; 272 V v; 273 makeEmpty(v); 274 V2 v2("hello"); 275 assert(test(v, v2)); 276 } 277 { 278 using V = std::variant<int, MakeEmptyT>; 279 using V2 = std::variant<long, std::string, void *>; 280 V v; 281 makeEmpty(v); 282 V2 v2("hello"); 283 assert(test(v2, v)); 284 } 285 { 286 using V = std::variant<int, MakeEmptyT>; 287 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 288 V v; 289 makeEmpty(v); 290 V2 v2; 291 makeEmpty(v2); 292 assert(test(v, v2)); 293 } 294 #endif 295 } 296 297 // See https://bugs.llvm.org/show_bug.cgi?id=31916 298 void test_caller_accepts_nonconst() { 299 struct A {}; 300 struct Visitor { 301 void operator()(A&) {} 302 }; 303 std::variant<A> v; 304 std::visit(Visitor{}, v); 305 } 306 307 int main(int, char**) { 308 test_call_operator_forwarding(); 309 test_argument_forwarding(); 310 test_constexpr(); 311 test_exceptions(); 312 test_caller_accepts_nonconst(); 313 314 return 0; 315 } 316