visit.pass.cpp (9053B)
1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // The LLVM Compiler Infrastructure 5 // 6 // This file is dual licensed under the MIT and the University of Illinois Open 7 // Source Licenses. See LICENSE.TXT for details. 8 // 9 //===----------------------------------------------------------------------===// 10 11 // UNSUPPORTED: c++98, c++03, c++11, c++14 12 13 // XFAIL: availability=macosx10.13 14 // XFAIL: availability=macosx10.12 15 // XFAIL: availability=macosx10.11 16 // XFAIL: availability=macosx10.10 17 // XFAIL: availability=macosx10.9 18 // XFAIL: availability=macosx10.8 19 // XFAIL: availability=macosx10.7 20 21 // <variant> 22 // template <class Visitor, class... Variants> 23 // constexpr see below visit(Visitor&& vis, Variants&&... vars); 24 25 #include <cassert> 26 #include <memory> 27 #include <string> 28 #include <type_traits> 29 #include <utility> 30 #include <variant> 31 32 #include "test_macros.h" 33 #include "type_id.h" 34 #include "variant_test_helpers.hpp" 35 36 enum CallType : unsigned { 37 CT_None, 38 CT_NonConst = 1, 39 CT_Const = 2, 40 CT_LValue = 4, 41 CT_RValue = 8 42 }; 43 44 inline constexpr CallType operator|(CallType LHS, CallType RHS) { 45 return static_cast<CallType>(static_cast<unsigned>(LHS) | 46 static_cast<unsigned>(RHS)); 47 } 48 49 struct ForwardingCallObject { 50 51 template <class... Args> bool operator()(Args &&...) & { 52 set_call<Args &&...>(CT_NonConst | CT_LValue); 53 return true; 54 } 55 56 template <class... Args> bool operator()(Args &&...) const & { 57 set_call<Args &&...>(CT_Const | CT_LValue); 58 return true; 59 } 60 61 // Don't allow the call operator to be invoked as an rvalue. 62 template <class... Args> bool operator()(Args &&...) && { 63 set_call<Args &&...>(CT_NonConst | CT_RValue); 64 return true; 65 } 66 67 template <class... Args> bool operator()(Args &&...) const && { 68 set_call<Args &&...>(CT_Const | CT_RValue); 69 return true; 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 148 void test_argument_forwarding() { 149 using Fn = ForwardingCallObject; 150 Fn obj{}; 151 const auto Val = CT_LValue | CT_NonConst; 152 { // single argument - value type 153 using V = std::variant<int>; 154 V v(42); 155 const V &cv = v; 156 std::visit(obj, v); 157 assert(Fn::check_call<int &>(Val)); 158 std::visit(obj, cv); 159 assert(Fn::check_call<const int &>(Val)); 160 std::visit(obj, std::move(v)); 161 assert(Fn::check_call<int &&>(Val)); 162 std::visit(obj, std::move(cv)); 163 assert(Fn::check_call<const int &&>(Val)); 164 } 165 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 166 { // single argument - lvalue reference 167 using V = std::variant<int &>; 168 int x = 42; 169 V v(x); 170 const V &cv = v; 171 std::visit(obj, v); 172 assert(Fn::check_call<int &>(Val)); 173 std::visit(obj, cv); 174 assert(Fn::check_call<int &>(Val)); 175 std::visit(obj, std::move(v)); 176 assert(Fn::check_call<int &>(Val)); 177 std::visit(obj, std::move(cv)); 178 assert(Fn::check_call<int &>(Val)); 179 } 180 { // single argument - rvalue reference 181 using V = std::variant<int &&>; 182 int x = 42; 183 V v(std::move(x)); 184 const V &cv = v; 185 std::visit(obj, v); 186 assert(Fn::check_call<int &>(Val)); 187 std::visit(obj, cv); 188 assert(Fn::check_call<int &>(Val)); 189 std::visit(obj, std::move(v)); 190 assert(Fn::check_call<int &&>(Val)); 191 std::visit(obj, std::move(cv)); 192 assert(Fn::check_call<int &&>(Val)); 193 } 194 { // multi argument - multi variant 195 using S = const std::string &; 196 using V = std::variant<int, S, long &&>; 197 const std::string str = "hello"; 198 long l = 43; 199 V v1(42); 200 const V &cv1 = v1; 201 V v2(str); 202 const V &cv2 = v2; 203 V v3(std::move(l)); 204 const V &cv3 = v3; 205 std::visit(obj, v1, v2, v3); 206 assert((Fn::check_call<int &, S, long &>(Val))); 207 std::visit(obj, cv1, cv2, std::move(v3)); 208 assert((Fn::check_call<const int &, S, long &&>(Val))); 209 } 210 #endif 211 } 212 213 struct ReturnFirst { 214 template <class... Args> constexpr int operator()(int f, Args &&...) const { 215 return f; 216 } 217 }; 218 219 struct ReturnArity { 220 template <class... Args> constexpr int operator()(Args &&...) const { 221 return sizeof...(Args); 222 } 223 }; 224 225 void test_constexpr() { 226 constexpr ReturnFirst obj{}; 227 constexpr ReturnArity aobj{}; 228 { 229 using V = std::variant<int>; 230 constexpr V v(42); 231 static_assert(std::visit(obj, v) == 42, ""); 232 } 233 { 234 using V = std::variant<short, long, char>; 235 constexpr V v(42l); 236 static_assert(std::visit(obj, v) == 42, ""); 237 } 238 { 239 using V1 = std::variant<int>; 240 using V2 = std::variant<int, char *, long long>; 241 using V3 = std::variant<bool, int, int>; 242 constexpr V1 v1; 243 constexpr V2 v2(nullptr); 244 constexpr V3 v3; 245 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 246 } 247 { 248 using V1 = std::variant<int>; 249 using V2 = std::variant<int, char *, long long>; 250 using V3 = std::variant<void *, int, int>; 251 constexpr V1 v1; 252 constexpr V2 v2(nullptr); 253 constexpr V3 v3; 254 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 255 } 256 } 257 258 void test_exceptions() { 259 #ifndef TEST_HAS_NO_EXCEPTIONS 260 ReturnArity obj{}; 261 auto test = [&](auto &&... args) { 262 try { 263 std::visit(obj, args...); 264 } catch (const std::bad_variant_access &) { 265 return true; 266 } catch (...) { 267 } 268 return false; 269 }; 270 { 271 using V = std::variant<int, MakeEmptyT>; 272 V v; 273 makeEmpty(v); 274 assert(test(v)); 275 } 276 { 277 using V = std::variant<int, MakeEmptyT>; 278 using V2 = std::variant<long, std::string, void *>; 279 V v; 280 makeEmpty(v); 281 V2 v2("hello"); 282 assert(test(v, v2)); 283 } 284 { 285 using V = std::variant<int, MakeEmptyT>; 286 using V2 = std::variant<long, std::string, void *>; 287 V v; 288 makeEmpty(v); 289 V2 v2("hello"); 290 assert(test(v2, v)); 291 } 292 { 293 using V = std::variant<int, MakeEmptyT>; 294 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 295 V v; 296 makeEmpty(v); 297 V2 v2; 298 makeEmpty(v2); 299 assert(test(v, v2)); 300 } 301 #endif 302 } 303 304 // See https://bugs.llvm.org/show_bug.cgi?id=31916 305 void test_caller_accepts_nonconst() { 306 struct A {}; 307 struct Visitor { 308 void operator()(A&) {} 309 }; 310 std::variant<A> v; 311 std::visit(Visitor{}, v); 312 } 313 314 int main() { 315 test_call_operator_forwarding(); 316 test_argument_forwarding(); 317 test_constexpr(); 318 test_exceptions(); 319 test_caller_accepts_nonconst(); 320 }