// -*- C++ -*- //===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14 // Throwing bad_variant_access is supported starting in macosx10.13 // XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions // XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions // XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions // XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions // // template // constexpr see below visit(Visitor&& vis, Variants&&... vars); #include #include #include #include #include #include #include "test_macros.h" #include "type_id.h" #include "variant_test_helpers.h" enum CallType : unsigned { CT_None, CT_NonConst = 1, CT_Const = 2, CT_LValue = 4, CT_RValue = 8 }; inline constexpr CallType operator|(CallType LHS, CallType RHS) { return static_cast(static_cast(LHS) | static_cast(RHS)); } struct ForwardingCallObject { template ForwardingCallObject& operator()(Args&&...) & { set_call(CT_NonConst | CT_LValue); return *this; } template const ForwardingCallObject& operator()(Args&&...) const & { set_call(CT_Const | CT_LValue); return *this; } template ForwardingCallObject&& operator()(Args&&...) && { set_call(CT_NonConst | CT_RValue); return std::move(*this); } template const ForwardingCallObject&& operator()(Args&&...) const && { set_call(CT_Const | CT_RValue); return std::move(*this); } template static void set_call(CallType type) { assert(last_call_type == CT_None); assert(last_call_args == nullptr); last_call_type = type; last_call_args = std::addressof(makeArgumentID()); } template static bool check_call(CallType type) { bool result = last_call_type == type && last_call_args && *last_call_args == makeArgumentID(); last_call_type = CT_None; last_call_args = nullptr; return result; } static CallType last_call_type; static const TypeID *last_call_args; }; CallType ForwardingCallObject::last_call_type = CT_None; const TypeID *ForwardingCallObject::last_call_args = nullptr; void test_call_operator_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; const Fn &cobj = obj; { // test call operator forwarding - no variant std::visit(obj); assert(Fn::check_call<>(CT_NonConst | CT_LValue)); std::visit(cobj); assert(Fn::check_call<>(CT_Const | CT_LValue)); std::visit(std::move(obj)); assert(Fn::check_call<>(CT_NonConst | CT_RValue)); std::visit(std::move(cobj)); assert(Fn::check_call<>(CT_Const | CT_RValue)); } { // test call operator forwarding - single variant, single arg using V = std::variant; V v(42); std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); std::visit(std::move(cobj), v); assert(Fn::check_call(CT_Const | CT_RValue)); } { // test call operator forwarding - single variant, multi arg using V = std::variant; V v(42l); std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); std::visit(std::move(cobj), v); assert(Fn::check_call(CT_Const | CT_RValue)); } { // test call operator forwarding - multi variant, multi arg using V = std::variant; using V2 = std::variant; V v(42l); V2 v2("hello"); std::visit(obj, v, v2); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v, v2); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v, v2); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v, v2); assert((Fn::check_call(CT_Const | CT_RValue))); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_RValue))); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); std::visit(std::move(obj), v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_RValue))); std::visit(std::move(cobj), v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_RValue))); } } void test_argument_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; const auto Val = CT_LValue | CT_NonConst; { // single argument - value type using V = std::variant; V v(42); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) { // single argument - lvalue reference using V = std::variant; int x = 42; V v(x); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } { // single argument - rvalue reference using V = std::variant; int x = 42; V v(std::move(x)); const V &cv = v; std::visit(obj, v); assert(Fn::check_call(Val)); std::visit(obj, cv); assert(Fn::check_call(Val)); std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } #endif { // multi argument - multi variant using V = std::variant; V v1(42), v2("hello"), v3(43l); std::visit(obj, v1, v2, v3); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3)); assert((Fn::check_call(Val))); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); assert((Fn::check_call(Val))); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(Val))); std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); assert((Fn::check_call(Val))); } } void test_return_type() { using Fn = ForwardingCallObject; Fn obj{}; const Fn &cobj = obj; { // test call operator forwarding - no variant static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - single variant, single arg using V = std::variant; V v(42); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - single variant, multi arg using V = std::variant; V v(42l); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { // test call operator forwarding - multi variant, multi arg using V = std::variant; using V2 = std::variant; V v(42l); V2 v2("hello"); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { using V = std::variant; V v1(42l), v2("hello"), v3(101), v4(1.1); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } { using V = std::variant; V v1(42l), v2("hello"), v3(nullptr), v4(1.1); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } } struct ReturnFirst { template constexpr int operator()(int f, Args &&...) const { return f; } }; struct ReturnArity { template constexpr int operator()(Args &&...) const { return sizeof...(Args); } }; void test_constexpr() { constexpr ReturnFirst obj{}; constexpr ReturnArity aobj{}; { using V = std::variant; constexpr V v(42); static_assert(std::visit(obj, v) == 42, ""); } { using V = std::variant; constexpr V v(42l); static_assert(std::visit(obj, v) == 42, ""); } { using V1 = std::variant; using V2 = std::variant; using V3 = std::variant; constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); } { using V1 = std::variant; using V2 = std::variant; using V3 = std::variant; constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); } { using V = std::variant; constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); } { using V = std::variant; constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); } } void test_exceptions() { #ifndef TEST_HAS_NO_EXCEPTIONS ReturnArity obj{}; auto test = [&](auto &&... args) { try { std::visit(obj, args...); } catch (const std::bad_variant_access &) { return true; } catch (...) { } return false; }; { using V = std::variant; V v; makeEmpty(v); assert(test(v)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2("hello"); assert(test(v, v2)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2("hello"); assert(test(v2, v)); } { using V = std::variant; using V2 = std::variant; V v; makeEmpty(v); V2 v2; makeEmpty(v2); assert(test(v, v2)); } { using V = std::variant; V v1(42l), v2(101), v3(202), v4(1.1); makeEmpty(v1); assert(test(v1, v2, v3, v4)); } { using V = std::variant; V v1(42l), v2(101), v3(202), v4(1.1); makeEmpty(v1); makeEmpty(v2); makeEmpty(v3); makeEmpty(v4); assert(test(v1, v2, v3, v4)); } #endif } // See https://bugs.llvm.org/show_bug.cgi?id=31916 void test_caller_accepts_nonconst() { struct A {}; struct Visitor { void operator()(A&) {} }; std::variant v; std::visit(Visitor{}, v); } int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); test_return_type(); test_constexpr(); test_exceptions(); test_caller_accepts_nonconst(); return 0; }