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 145 void test_argument_forwarding() { 146 using Fn = ForwardingCallObject; 147 Fn obj{}; 148 const auto Val = CT_LValue | CT_NonConst; 149 { // single argument - value type 150 using V = std::variant<int>; 151 V v(42); 152 const V &cv = v; 153 std::visit(obj, v); 154 assert(Fn::check_call<int &>(Val)); 155 std::visit(obj, cv); 156 assert(Fn::check_call<const int &>(Val)); 157 std::visit(obj, std::move(v)); 158 assert(Fn::check_call<int &&>(Val)); 159 std::visit(obj, std::move(cv)); 160 assert(Fn::check_call<const int &&>(Val)); 161 } 162 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 163 { // single argument - lvalue reference 164 using V = std::variant<int &>; 165 int x = 42; 166 V v(x); 167 const V &cv = v; 168 std::visit(obj, v); 169 assert(Fn::check_call<int &>(Val)); 170 std::visit(obj, cv); 171 assert(Fn::check_call<int &>(Val)); 172 std::visit(obj, std::move(v)); 173 assert(Fn::check_call<int &>(Val)); 174 std::visit(obj, std::move(cv)); 175 assert(Fn::check_call<int &>(Val)); 176 } 177 { // single argument - rvalue reference 178 using V = std::variant<int &&>; 179 int x = 42; 180 V v(std::move(x)); 181 const V &cv = v; 182 std::visit(obj, v); 183 assert(Fn::check_call<int &>(Val)); 184 std::visit(obj, cv); 185 assert(Fn::check_call<int &>(Val)); 186 std::visit(obj, std::move(v)); 187 assert(Fn::check_call<int &&>(Val)); 188 std::visit(obj, std::move(cv)); 189 assert(Fn::check_call<int &&>(Val)); 190 } 191 { // multi argument - multi variant 192 using S = const std::string &; 193 using V = std::variant<int, S, long &&>; 194 const std::string str = "hello"; 195 long l = 43; 196 V v1(42); 197 const V &cv1 = v1; 198 V v2(str); 199 const V &cv2 = v2; 200 V v3(std::move(l)); 201 const V &cv3 = v3; 202 std::visit(obj, v1, v2, v3); 203 assert((Fn::check_call<int &, S, long &>(Val))); 204 std::visit(obj, cv1, cv2, std::move(v3)); 205 assert((Fn::check_call<const int &, S, long &&>(Val))); 206 } 207 #endif 208 } 209 210 struct ReturnFirst { 211 template <class... Args> constexpr int operator()(int f, Args &&...) const { 212 return f; 213 } 214 }; 215 216 struct ReturnArity { 217 template <class... Args> constexpr int operator()(Args &&...) const { 218 return sizeof...(Args); 219 } 220 }; 221 222 void test_constexpr() { 223 constexpr ReturnFirst obj{}; 224 constexpr ReturnArity aobj{}; 225 { 226 using V = std::variant<int>; 227 constexpr V v(42); 228 static_assert(std::visit(obj, v) == 42, ""); 229 } 230 { 231 using V = std::variant<short, long, char>; 232 constexpr V v(42l); 233 static_assert(std::visit(obj, v) == 42, ""); 234 } 235 { 236 using V1 = std::variant<int>; 237 using V2 = std::variant<int, char *, long long>; 238 using V3 = std::variant<bool, int, int>; 239 constexpr V1 v1; 240 constexpr V2 v2(nullptr); 241 constexpr V3 v3; 242 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 243 } 244 { 245 using V1 = std::variant<int>; 246 using V2 = std::variant<int, char *, long long>; 247 using V3 = std::variant<void *, int, int>; 248 constexpr V1 v1; 249 constexpr V2 v2(nullptr); 250 constexpr V3 v3; 251 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 252 } 253 } 254 255 void test_exceptions() { 256 #ifndef TEST_HAS_NO_EXCEPTIONS 257 ReturnArity obj{}; 258 auto test = [&](auto &&... args) { 259 try { 260 std::visit(obj, args...); 261 } catch (const std::bad_variant_access &) { 262 return true; 263 } catch (...) { 264 } 265 return false; 266 }; 267 { 268 using V = std::variant<int, MakeEmptyT>; 269 V v; 270 makeEmpty(v); 271 assert(test(v)); 272 } 273 { 274 using V = std::variant<int, MakeEmptyT>; 275 using V2 = std::variant<long, std::string, void *>; 276 V v; 277 makeEmpty(v); 278 V2 v2("hello"); 279 assert(test(v, v2)); 280 } 281 { 282 using V = std::variant<int, MakeEmptyT>; 283 using V2 = std::variant<long, std::string, void *>; 284 V v; 285 makeEmpty(v); 286 V2 v2("hello"); 287 assert(test(v2, v)); 288 } 289 { 290 using V = std::variant<int, MakeEmptyT>; 291 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 292 V v; 293 makeEmpty(v); 294 V2 v2; 295 makeEmpty(v2); 296 assert(test(v, v2)); 297 } 298 #endif 299 } 300 301 // See https://bugs.llvm.org/show_bug.cgi?id=31916 302 void test_caller_accepts_nonconst() { 303 struct A {}; 304 struct Visitor { 305 void operator()(A&) {} 306 }; 307 std::variant<A> v; 308 std::visit(Visitor{}, v); 309 } 310 311 int main(int, char**) { 312 test_call_operator_forwarding(); 313 test_argument_forwarding(); 314 test_constexpr(); 315 test_exceptions(); 316 test_caller_accepts_nonconst(); 317 318 return 0; 319 } 320