diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -59,7 +59,7 @@ "`P0595R2 `__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0" "`P0602R4 `__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0" "`P0608R3 `__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0" -"`P0655R1 `__","LWG","visit: Explicit Return Type for visit","San Diego","* *","" +"`P0655R1 `__","LWG","visit: Explicit Return Type for visit","San Diego","|Complete|","12.0" "`P0771R1 `__","LWG","std::function move constructor should be noexcept","San Diego","|Complete|","6.0" "`P0896R4 `__","LWG","The One Ranges Proposal","San Diego","* *","" "`P0899R1 `__","LWG","P0899R1 - LWG 3016 is not a defect","San Diego","|Nothing To Do|","" diff --git a/libcxx/include/variant b/libcxx/include/variant --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -169,6 +169,9 @@ template constexpr see below visit(Visitor&&, Variants&&...); + template + constexpr R visit(Visitor&&, Variants&&...); // since C++20 + // 20.7.7, class monostate struct monostate; @@ -583,6 +586,16 @@ __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), _VSTD::forward<_Vs>(__vs)...); } +#if _LIBCPP_STD_VER > 17 + template + inline _LIBCPP_INLINE_VISIBILITY + static constexpr _Rp __visit_value(_Visitor&& __visitor, + _Vs&&... __vs) { + return __visit_alt( + __make_value_visitor<_Rp>(_VSTD::forward<_Visitor>(__visitor)), + _VSTD::forward<_Vs>(__vs)...); + } +#endif private: template @@ -605,12 +618,43 @@ _Visitor&& __visitor; }; +#if _LIBCPP_STD_VER > 17 + template + struct __value_visitor_return_type { + template + inline _LIBCPP_INLINE_VISIBILITY + constexpr _Rp operator()(_Alts&&... __alts) const { + __std_visit_exhaustive_visitor_check< + _Visitor, + decltype((_VSTD::forward<_Alts>(__alts).__value))...>(); + if constexpr (is_void_v<_Rp>) { + _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Alts>(__alts).__value...); + } + else { + return _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Alts>(__alts).__value...); + } + } + + _Visitor&& __visitor; + }; +#endif + template inline _LIBCPP_INLINE_VISIBILITY static constexpr auto __make_value_visitor(_Visitor&& __visitor) { return __value_visitor<_Visitor>{_VSTD::forward<_Visitor>(__visitor)}; } + +#if _LIBCPP_STD_VER > 17 + template + inline _LIBCPP_INLINE_VISIBILITY + static constexpr auto __make_value_visitor(_Visitor&& __visitor) { + return __value_visitor_return_type<_Rp, _Visitor>{_VSTD::forward<_Visitor>(__visitor)}; + } }; +#endif } // namespace __visitation @@ -1594,18 +1638,37 @@ template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { - using __variant_detail::__visitation::__variant; - bool __results[] = {__vs.valueless_by_exception()...}; - for (bool __result : __results) { - if (__result) { +constexpr void __throw_if_valueless(_Visitor&& __visitor, _Vs&&... __vs) { + const bool __valueless = (... || __vs.valueless_by_exception()); + if (__valueless) { __throw_bad_variant_access(); - } } +} + +template +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS +constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { + using __variant_detail::__visitation::__variant; + _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor), _VSTD::forward<_Vs>(__vs)...); } +#if _LIBCPP_STD_VER > 17 +template +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS +constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) { + using __variant_detail::__visitation::__variant; + _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); + return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor), + _VSTD::forward<_Vs>(__vs)...); +} +#endif + struct _LIBCPP_TEMPLATE_VIS monostate {}; inline _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp @@ -32,6 +32,10 @@ std::variant*, int> v = nullptr; std::visit([](auto){}, v); std::visit([](auto) -> Holder* { return nullptr; }, v); +#if TEST_STD_VER > 17 + std::visit([](auto){}, v); + std::visit([](auto) -> Holder* { return nullptr; }, v); +#endif } return true; } diff --git a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp @@ -27,70 +27,8 @@ #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{}; @@ -296,18 +234,6 @@ } } -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{}; diff --git a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp copy from libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp copy to libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 // Throwing bad_variant_access is supported starting in macosx10.13 // XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions @@ -16,8 +16,8 @@ // XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions // -// template -// constexpr see below visit(Visitor&& vis, Variants&&... vars); +// template +// constexpr R visit(Visitor&& vis, Variants&&... vars); #include #include @@ -27,106 +27,45 @@ #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; - +template void test_call_operator_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; const Fn &cobj = obj; { // test call operator forwarding - no variant - std::visit(obj); + std::visit(obj); assert(Fn::check_call<>(CT_NonConst | CT_LValue)); - std::visit(cobj); + std::visit(cobj); assert(Fn::check_call<>(CT_Const | CT_LValue)); - std::visit(std::move(obj)); + std::visit(std::move(obj)); assert(Fn::check_call<>(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj)); + 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); + std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); - std::visit(cobj, v); + std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); - std::visit(std::move(obj), v); + std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj), v); + 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); + std::visit(obj, v); assert(Fn::check_call(CT_NonConst | CT_LValue)); - std::visit(cobj, v); + std::visit(cobj, v); assert(Fn::check_call(CT_Const | CT_LValue)); - std::visit(std::move(obj), v); + std::visit(std::move(obj), v); assert(Fn::check_call(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj), v); + std::visit(std::move(cobj), v); assert(Fn::check_call(CT_Const | CT_RValue)); } { // test call operator forwarding - multi variant, multi arg @@ -134,41 +73,42 @@ using V2 = std::variant; V v(42l); V2 v2("hello"); - std::visit(obj, v, v2); + std::visit(obj, v, v2); assert((Fn::check_call(CT_NonConst | CT_LValue))); - std::visit(cobj, v, v2); + std::visit(cobj, v, v2); assert((Fn::check_call(CT_Const | CT_LValue))); - std::visit(std::move(obj), v, v2); + std::visit(std::move(obj), v, v2); assert((Fn::check_call(CT_NonConst | CT_RValue))); - std::visit(std::move(cobj), v, v2); + 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); + std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); - std::visit(cobj, v1, v2, v3, v4); + std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); - std::visit(std::move(obj), v1, v2, v3, v4); + 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); + 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); + std::visit(obj, v1, v2, v3, v4); assert((Fn::check_call(CT_NonConst | CT_LValue))); - std::visit(cobj, v1, v2, v3, v4); + std::visit(cobj, v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_LValue))); - std::visit(std::move(obj), v1, v2, v3, v4); + 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); + std::visit(std::move(cobj), v1, v2, v3, v4); assert((Fn::check_call(CT_Const | CT_RValue))); } } +template void test_argument_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; @@ -177,13 +117,13 @@ using V = std::variant; V v(42); const V &cv = v; - std::visit(obj, v); + std::visit(obj, v); assert(Fn::check_call(Val)); - std::visit(obj, cv); + std::visit(obj, cv); assert(Fn::check_call(Val)); - std::visit(obj, std::move(v)); + std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); - std::visit(obj, std::move(cv)); + std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) @@ -192,13 +132,13 @@ int x = 42; V v(x); const V &cv = v; - std::visit(obj, v); + std::visit(obj, v); assert(Fn::check_call(Val)); - std::visit(obj, cv); + std::visit(obj, cv); assert(Fn::check_call(Val)); - std::visit(obj, std::move(v)); + std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); - std::visit(obj, std::move(cv)); + std::visit(obj, std::move(cv)); assert(Fn::check_call(Val)); } { // single argument - rvalue reference @@ -206,120 +146,152 @@ int x = 42; V v(std::move(x)); const V &cv = v; - std::visit(obj, v); + std::visit(obj, v); assert(Fn::check_call(Val)); - std::visit(obj, cv); + std::visit(obj, cv); assert(Fn::check_call(Val)); - std::visit(obj, std::move(v)); + std::visit(obj, std::move(v)); assert(Fn::check_call(Val)); - std::visit(obj, std::move(cv)); + 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); + 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)); + 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); + 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)); + 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); + 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)); + std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); assert((Fn::check_call(Val))); } } +template 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); + static_assert(std::is_same_v(obj)), ReturnType>); + static_assert(std::is_same_v(cobj)), ReturnType>); + static_assert(std::is_same_v(std::move(obj))), ReturnType>); + static_assert(std::is_same_v(std::move(cobj))), ReturnType>); } { // 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); + static_assert(std::is_same_v(obj, v)), ReturnType>); + static_assert(std::is_same_v(cobj, v)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v)), ReturnType>); } { // 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); + static_assert(std::is_same_v(obj, v)), ReturnType>); + static_assert(std::is_same_v(cobj, v)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v)), ReturnType>); } { // 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); + static_assert(std::is_same_v(obj, v, v2)), ReturnType>); + static_assert(std::is_same_v(cobj, v, v2)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v, v2)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v, v2)), ReturnType>); } { 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); + static_assert(std::is_same_v(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v1, v2, v3, v4)), ReturnType>); } { 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); + static_assert(std::is_same_v(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v(std::move(cobj), v1, v2, v3, v4)), ReturnType>); } } -struct ReturnFirst { - template constexpr int operator()(int f, Args &&...) const { - return f; +void test_constexpr_void() { + constexpr ReturnFirst obj{}; + constexpr ReturnArity aobj{}; + { + using V = std::variant; + constexpr V v(42); + static_assert((std::visit(obj, v), 42) == 42, ""); } -}; - -struct ReturnArity { - template constexpr int operator()(Args &&...) const { - return sizeof...(Args); + { + using V = std::variant; + constexpr V v(42l); + static_assert((std::visit(obj, v), 42) == 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) == 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) == 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) == 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) == 4, ""); + } +} -void test_constexpr() { +void test_constexpr_int() { constexpr ReturnFirst obj{}; constexpr ReturnArity aobj{}; { using V = std::variant; constexpr V v(42); - static_assert(std::visit(obj, v) == 42, ""); + static_assert(std::visit(obj, v) == 42, ""); } { using V = std::variant; constexpr V v(42l); - static_assert(std::visit(obj, v) == 42, ""); + static_assert(std::visit(obj, v) == 42, ""); } { using V1 = std::variant; @@ -328,7 +300,7 @@ constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; - static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); + static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); } { using V1 = std::variant; @@ -337,26 +309,27 @@ constexpr V1 v1; constexpr V2 v2(nullptr); constexpr V3 v3; - static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); + 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, ""); + 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, ""); + static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, ""); } } +template void test_exceptions() { #ifndef TEST_HAS_NO_EXCEPTIONS ReturnArity obj{}; auto test = [&](auto &&... args) { try { - std::visit(obj, args...); + std::visit(obj, args...); } catch (const std::bad_variant_access &) { return true; } catch (...) { @@ -413,22 +386,45 @@ } // See https://bugs.llvm.org/show_bug.cgi?id=31916 +template void test_caller_accepts_nonconst() { struct A {}; struct Visitor { - void operator()(A&) {} + auto operator()(A&) { + if constexpr (!std::is_void_v) + { + return ReturnType{}; + } + } }; std::variant v; - std::visit(Visitor{}, v); + std::visit(Visitor{}, v); +} + +void test_constexpr_explicit_side_effect() { + auto test_lambda = [](int arg) constexpr { + std::variant v = 101; + std::visit([arg](int& x) constexpr { x = arg; }, v); + return std::get(v); + }; + + static_assert(test_lambda(202) == 202, ""); } int main(int, char**) { - test_call_operator_forwarding(); - test_argument_forwarding(); - test_return_type(); - test_constexpr(); - test_exceptions(); - test_caller_accepts_nonconst(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_void(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_int(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_constexpr_explicit_side_effect(); return 0; } diff --git a/libcxx/test/support/variant_test_helpers.h b/libcxx/test/support/variant_test_helpers.h --- a/libcxx/test/support/variant_test_helpers.h +++ b/libcxx/test/support/variant_test_helpers.h @@ -14,6 +14,7 @@ #include #include "test_macros.h" +#include "type_id.h" #if TEST_STD_VER <= 14 #error This file requires C++17 @@ -85,5 +86,83 @@ } #endif // TEST_HAS_NO_EXCEPTIONS +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; + } + + // To check explicit return type for visit + constexpr operator int() const + { + return 0; + } + + 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; + +struct ReturnFirst { + template constexpr int operator()(int f, Args &&...) const { + return f; + } +}; + +struct ReturnArity { + template constexpr int operator()(Args &&...) const { + return sizeof...(Args); + } +}; #endif // SUPPORT_VARIANT_TEST_HELPERS_H