diff --git a/libcxx/include/variant b/libcxx/include/variant --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -354,9 +354,9 @@ enum class _Trait { _TriviallyAvailable, _Available, _Unavailable }; -template class _IsTriviallyAvailable, - template class _IsAvailable> +template class _IsTriviallyAvailable, + template class _IsAvailable> constexpr _Trait __trait = _IsTriviallyAvailable<_Tp>::value ? _Trait::_TriviallyAvailable @@ -373,7 +373,7 @@ return __result; } -template +template struct __traits { static constexpr _Trait __copy_constructible_trait = __common_trait({__trait<_Types, @@ -434,183 +434,285 @@ namespace __visitation { -struct __base { - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr decltype(auto) - __visit_alt_at(size_t __index, _Visitor&& __visitor, _Vs&&... __vs) { - constexpr auto __fdiagonal = - __make_fdiagonal<_Visitor&&, - decltype(_VSTD::forward<_Vs>(__vs).__as_base())...>(); - return __fdiagonal[__index](_VSTD::forward<_Visitor>(__visitor), - _VSTD::forward<_Vs>(__vs).__as_base()...); - } - - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor, - _Vs&&... __vs) { - constexpr auto __fmatrix = - __make_fmatrix<_Visitor&&, - decltype(_VSTD::forward<_Vs>(__vs).__as_base())...>(); - return __at(__fmatrix, __vs.index()...)( - _VSTD::forward<_Visitor>(__visitor), - _VSTD::forward<_Vs>(__vs).__as_base()...); - } +#define _LIBCPP_VARIANT_CASES_4(_Case, _Base) \ + _Case(_Base + 0) \ + _Case(_Base + 1) \ + _Case(_Base + 2) \ + _Case(_Base + 3) + +#define _LIBCPP_VARIANT_CASES_16(_Case, _Base) \ + _LIBCPP_VARIANT_CASES_4(_Case, _Base + 4 * 0) \ + _LIBCPP_VARIANT_CASES_4(_Case, _Base + 4 * 1) \ + _LIBCPP_VARIANT_CASES_4(_Case, _Base + 4 * 2) \ + _LIBCPP_VARIANT_CASES_4(_Case, _Base + 4 * 3) + +#define _LIBCPP_VARIANT_CASES_64(_Case, _Base) \ + _LIBCPP_VARIANT_CASES_16(_Case, _Base + 16 * 0) \ + _LIBCPP_VARIANT_CASES_16(_Case, _Base + 16 * 1) \ + _LIBCPP_VARIANT_CASES_16(_Case, _Base + 16 * 2) \ + _LIBCPP_VARIANT_CASES_16(_Case, _Base + 16 * 3) + +#define _LIBCPP_VARIANT_CASES_256(_Case, _Base) \ + _LIBCPP_VARIANT_CASES_64(_Case, _Base + 64 * 0) \ + _LIBCPP_VARIANT_CASES_64(_Case, _Base + 64 * 1) \ + _LIBCPP_VARIANT_CASES_64(_Case, _Base + 64 * 2) \ + _LIBCPP_VARIANT_CASES_64(_Case, _Base + 64 * 3) + +#define _LIBCPP_VARIANT_CASES_1024(_Case, _Base) \ + _LIBCPP_VARIANT_CASES_256(_Case, _Base + 256 * 0) \ + _LIBCPP_VARIANT_CASES_256(_Case, _Base + 256 * 1) \ + _LIBCPP_VARIANT_CASES_256(_Case, _Base + 256 * 2) \ + _LIBCPP_VARIANT_CASES_256(_Case, _Base + 256 * 3) + +#define _LIBCPP_VARIANT_CASES_IMPL(_NumCases, _Case) \ + _LIBCPP_VARIANT_CASES_##_NumCases(_Case, 0) + +#define _LIBCPP_VARIANT_CASES(_NumCases, _Case) \ + _LIBCPP_VARIANT_CASES_IMPL(_NumCases, _Case) + +#define _LIBCPP_VARIANT_SWITCH_VISIT_MAX 1024 + +template +static constexpr void __fill_cartesian_impl( + _Iter __iter, _Fp __f, index_sequence<_Is...>) { + *__iter = __f(integral_constant{}...); +} -private: - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr const _Tp& __at(const _Tp& __elem) { return __elem; } +template +static constexpr void __fill_cartesian_impl( + _Iter __iter, _Fp __f, index_sequence<_Is...>, index_sequence<_Js...>, _Ls... __ls) { + constexpr size_t _Mp = (1 * ... * _Ls::size()); + (__fill_cartesian_impl( + __iter + _Js * _Mp, __f, index_sequence<_Is..., _Js>{}, __ls...), ...); +} - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto&& __at(const array<_Tp, _Np>& __elems, - size_t __index, _Indices... __indices) { - return __at(__elems[__index], __indices...); - } +template +static constexpr void __fill_cartesian(_Iter __iter, _Fp __f) { + __fill_cartesian_impl( + __iter, __f, index_sequence<>{}, make_index_sequence<_Ns>{}...); +} - template - static constexpr void __std_visit_visitor_return_type_check() { - static_assert( - __all...>::value, - "`std::visit` requires the visitor to have a single return type."); +template +struct __multi { + static constexpr size_t + __index(const size_t (&__is)[sizeof...(_Ns) + 1]) noexcept { + constexpr size_t __ns[] = {_Ns..., 1}; + size_t __result = 0; + for (size_t __i = 0; __i < sizeof...(_Ns) + 1; ++__i) { + if (__builtin_expect(__is[__i] == static_cast(-1), 0)) { + return static_cast(-1); + } + __result += __is[__i]; + __result *= __ns[__i]; + } + return __result; } +}; - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_farray(_Fs&&... __fs) { - __std_visit_visitor_return_type_check<__uncvref_t<_Fs>...>(); - using __result = array...>, sizeof...(_Fs)>; - return __result{{_VSTD::forward<_Fs>(__fs)...}}; - } +template +struct __indices { + static constexpr auto __value = [] { + using _Tp = array; + array<_Tp, (1 * ... * _Ns)> __result = {}; + __fill_cartesian<_Ns...>(__result.begin(), + [](auto... __is) -> _Tp { return {__is...}; }); + return __result; + }(); +}; - template - struct __dispatcher { - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr decltype(auto) __dispatch(_Fp __f, _Vs... __vs) { - return __invoke_constexpr( - static_cast<_Fp>(__f), - __access::__base::__get_alt<_Is>(static_cast<_Vs>(__vs))...); - } +template +static constexpr auto __make_vtable_impl(_Fp __f, _Rp (*)(_Args...)) { + array<_Rp (*)(_Args...), (1 * ... * _Ns) + 1> __result = { + [](_Args...) -> _Rp { __throw_bad_variant_access(); } }; + __fill_cartesian<_Ns...>(__result.begin() + 1, __f); + return __result; +} - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_dispatch(index_sequence<_Is...>) { - return __dispatcher<_Is...>::template __dispatch<_Fp, _Vs...>; - } +template +static constexpr auto __make_vtable(_Fp __f) { + using _Tp = decltype(__f(std::integral_constant{}...)); + return __make_vtable_impl<_Ns...>(__f, _Tp{}); +} - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fdiagonal_impl() { - return __make_dispatch<_Fp, _Vs...>( - index_sequence<(__identity<_Vs>{}, _Ip)...>{}); +struct __base { + template + static constexpr bool __switch_visit() { + return +#ifdef _LIBCPP_DISABLE_SWITCH_VISIT_IN_VARIANT + false; +#else + size <= _LIBCPP_VARIANT_SWITCH_VISIT_MAX; +#endif } - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fdiagonal_impl(index_sequence<_Is...>) { - return __base::__make_farray(__make_fdiagonal_impl<_Is, _Fp, _Vs...>()...); + static constexpr decltype(auto) + __visit_alt_at(size_t __index, _Vis&& __vis, _Vp&& __v, _Wp&& __w) { + constexpr size_t size = __uncvref_t<_Vp>::__size(); + static_assert(size == __uncvref_t<_Wp>::__size()); + if constexpr (__switch_visit()) { +#define _LIBCPP_VARIANT_CASE(_Ip) \ + case _Ip: { \ + if constexpr (_Ip < size) { \ + return __invoke_constexpr( \ + _VSTD::forward<_Vis>(__vis), \ + __access::__base::__get_alt<_Ip>(_VSTD::forward<_Vp>(__v)), \ + __access::__base::__get_alt<_Ip>(_VSTD::forward<_Wp>(__w))); \ + } else { \ + _LIBCPP_UNREACHABLE(); \ + } \ } - - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fdiagonal() { - constexpr size_t _Np = __uncvref_t<_Vp>::__size(); - static_assert(__all<(_Np == __uncvref_t<_Vs>::__size())...>::value); - return __make_fdiagonal_impl<_Fp, _Vp, _Vs...>(make_index_sequence<_Np>{}); + switch (__index) { + _LIBCPP_VARIANT_CASES(_LIBCPP_VARIANT_SWITCH_VISIT_MAX, + _LIBCPP_VARIANT_CASE) + default: __throw_bad_variant_access(); + } +#undef _LIBCPP_VARIANT_CASE + } else { + constexpr auto __vtable = __make_vtable([](auto __i) { + return +[](_Vis&& __vis, _Vp&& __v, _Wp&& __w) -> decltype(auto) { + constexpr size_t _Ip = decltype(__i)::value; + return __invoke_constexpr( + _VSTD::forward<_Vis>(__vis), + __access::__base::__get_alt<_Ip>(_VSTD::forward<_Vp>(__v)), + __access::__base::__get_alt<_Ip>(_VSTD::forward<_Wp>(__w))); + }; + }); + return __vtable[__index + 1](_VSTD::forward<_Vis>(__vis), + _VSTD::forward<_Vp>(__v), + _VSTD::forward<_Wp>(__w)); + } } - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fmatrix_impl(index_sequence<_Is...> __is) { - return __make_dispatch<_Fp, _Vs...>(__is); + static constexpr decltype(auto) __visit_alt(_Vis&& __vis, _Vs&&... __vs) { + if constexpr (sizeof...(_Vs) == 0) { + return __invoke_constexpr(_VSTD::forward<_Vis>(__vis)); + } else { + return __visit_alt_impl(index_sequence_for<_Vs...>{}, + _VSTD::forward<_Vis>(__vis), + _VSTD::forward<_Vs>(__vs)...); + } } - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fmatrix_impl(index_sequence<_Is...>, - index_sequence<_Js...>, - _Ls... __ls) { - return __base::__make_farray(__make_fmatrix_impl<_Fp, _Vs...>( - index_sequence<_Is..., _Js>{}, __ls...)...); + static constexpr decltype(auto) + __visit_alt_impl(index_sequence<_Is...>, _Vis&& __vis, _Vs&&... __vs) { + constexpr size_t size = (1 * ... * __uncvref_t<_Vs>::__size()); + using __multi = __multi<__uncvref_t<_Vs>::__size()...>; + if constexpr (__switch_visit()) { + constexpr const auto& __itable = + __indices<__uncvref_t<_Vs>::__size()...>::__value; +#define _LIBCPP_VARIANT_CASE(_Ip) \ + case _Ip: { \ + if constexpr (_Ip < size) { \ + return __invoke_constexpr( \ + _VSTD::forward<_Vis>(__vis), \ + __access::__base::__get_alt<__itable[_Ip][_Is]>( \ + _VSTD::forward<_Vs>(__vs))...); \ + } else { \ + _LIBCPP_UNREACHABLE(); \ + } \ } - - template - inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_fmatrix() { - return __make_fmatrix_impl<_Fp, _Vs...>( - index_sequence<>{}, make_index_sequence<__uncvref_t<_Vs>::__size()>{}...); + switch (__multi::__index({__vs.index()...})) { + _LIBCPP_VARIANT_CASES(_LIBCPP_VARIANT_SWITCH_VISIT_MAX, + _LIBCPP_VARIANT_CASE) + default: __throw_bad_variant_access(); + } +#undef _LIBCPP_VARIANT_CASE + } else { + constexpr auto __vtable = + __make_vtable<__uncvref_t<_Vs>::__size()...>([](auto... __is) { + return +[](_Vis&& __vis, _Vs&&... __vs) -> decltype(auto) { + return __invoke_constexpr( + _VSTD::forward<_Vis>(__vis), + __access::__base::__get_alt( + _VSTD::forward<_Vs>(__vs))...); + }; + }); + return __vtable[__multi::__index({__vs.index()...}) + 1]( + _VSTD::forward<_Vis>(__vis), _VSTD::forward<_Vs>(__vs)...); + } } }; struct __variant { - template + template inline _LIBCPP_INLINE_VISIBILITY static constexpr decltype(auto) - __visit_alt_at(size_t __index, _Visitor&& __visitor, _Vs&&... __vs) { + __visit_alt_at(size_t __index, _Vis&& __vis, _Vp&& __v, _Wp&& __w) { return __base::__visit_alt_at(__index, - _VSTD::forward<_Visitor>(__visitor), - _VSTD::forward<_Vs>(__vs).__impl...); + _VSTD::forward<_Vis>(__vis), + _VSTD::forward<_Vp>(__v).__impl, + _VSTD::forward<_Wp>(__w).__impl); } - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor, - _Vs&&... __vs) { - return __base::__visit_alt(_VSTD::forward<_Visitor>(__visitor), + static constexpr decltype(auto) __visit_alt(_Vis&& __vis, _Vs&&... __vs) { + return __base::__visit_alt(_VSTD::forward<_Vis>(__vis), _VSTD::forward<_Vs>(__vs).__impl...); } - template + template inline _LIBCPP_INLINE_VISIBILITY static constexpr decltype(auto) - __visit_value_at(size_t __index, _Visitor&& __visitor, _Vs&&... __vs) { - return __visit_alt_at( - __index, - __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), - _VSTD::forward<_Vs>(__vs)...); + __visit_value_at(size_t __index, _Vis&& __vis, _Vp&& __v, _Wp&& __w) { + return __visit_alt_at(__index, + __make_value_visitor(_VSTD::forward<_Vis>(__vis)), + _VSTD::forward<_Vp>(__v), + _VSTD::forward<_Wp>(__w)); } - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr decltype(auto) __visit_value(_Visitor&& __visitor, - _Vs&&... __vs) { - return __visit_alt( - __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), - _VSTD::forward<_Vs>(__vs)...); + static constexpr decltype(auto) __visit_value(_Vis&& __vis, _Vs&&... __vs) { + return __visit_alt(__make_value_visitor(_VSTD::forward<_Vis>(__vis)), + _VSTD::forward<_Vs>(__vs)...); } private: - template + template + inline _LIBCPP_INLINE_VISIBILITY static constexpr void __std_visit_exhaustive_visitor_check() { - static_assert(is_invocable_v<_Visitor, _Values...>, + static_assert(is_invocable_v<_Vis, _Values...>, "`std::visit` requires the visitor to be exhaustive."); } - template + template struct __value_visitor { template inline _LIBCPP_INLINE_VISIBILITY constexpr decltype(auto) operator()(_Alts&&... __alts) const { __std_visit_exhaustive_visitor_check< - _Visitor, - decltype((_VSTD::forward<_Alts>(__alts).__value))...>(); - return __invoke_constexpr(_VSTD::forward<_Visitor>(__visitor), + _Vis, decltype((_VSTD::forward<_Alts>(__alts).__value))...>(); + return __invoke_constexpr(_VSTD::forward<_Vis>(__vis), _VSTD::forward<_Alts>(__alts).__value...); } - _Visitor&& __visitor; + _Vis&& __vis; }; - template + template inline _LIBCPP_INLINE_VISIBILITY - static constexpr auto __make_value_visitor(_Visitor&& __visitor) { - return __value_visitor<_Visitor>{_VSTD::forward<_Visitor>(__visitor)}; + static constexpr auto __make_value_visitor(_Vis&& __vis) { + return __value_visitor<_Vis>{_VSTD::forward<_Vis>(__vis)}; } }; +#undef _LIBCPP_VARIANT_SWITCH_VISIT_MAX +#undef _LIBCPP_VARIANT_CASES +#undef _LIBCPP_VARIANT_CASES_IMPL +#undef _LIBCPP_VARIANT_CASES_1024 +#undef _LIBCPP_VARIANT_CASES_256 +#undef _LIBCPP_VARIANT_CASES_64 +#undef _LIBCPP_VARIANT_CASES_16 +#undef _LIBCPP_VARIANT_CASES_4 +#undef _LIBCPP_VARIANT_CASES_0 + } // namespace __visitation template @@ -633,7 +735,7 @@ #define _LIBCPP_VARIANT_UNION(destructible_trait, destructor) \ template \ - union _LIBCPP_TEMPLATE_VIS __union { \ @@ -787,9 +889,9 @@ template inline _LIBCPP_INLINE_VISIBILITY static _Tp& __construct_alt(__alt<_Ip, _Tp>& __a, _Args&&... __args) { - ::new ((void*)_VSTD::addressof(__a)) + auto* result = ::new ((void*)_VSTD::addressof(__a)) __alt<_Ip, _Tp>(in_place, _VSTD::forward<_Args>(__args)...); - return __a.__value; + return result->__value; } template @@ -816,7 +918,7 @@ #define _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, \ move_constructor) \ template \ - class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>, \ + class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>, \ move_constructible_trait> \ : public __constructor<__traits<_Types...>> { \ using __base_type = __constructor<__traits<_Types...>>; \ @@ -856,7 +958,7 @@ #define _LIBCPP_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, \ copy_constructor) \ template \ - class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>, \ + class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>, \ copy_constructible_trait> \ : public __move_constructor<__traits<_Types...>> { \ using __base_type = __move_constructor<__traits<_Types...>>; \ @@ -902,7 +1004,7 @@ auto& __emplace(_Args&&... __args) { this->__destroy(); auto& __res = this->__construct_alt(__access::__base::__get_alt<_Ip>(*this), - _VSTD::forward<_Args>(__args)...); + _VSTD::forward<_Args>(__args)...); this->__index = _Ip; return __res; } @@ -955,7 +1057,7 @@ #define _LIBCPP_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, \ move_assignment) \ template \ - class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>, \ + class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>, \ move_assignable_trait> \ : public __assignment<__traits<_Types...>> { \ using __base_type = __assignment<__traits<_Types...>>; \ @@ -996,7 +1098,7 @@ #define _LIBCPP_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, \ copy_assignment) \ template \ - class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>, \ + class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>, \ copy_assignable_trait> \ : public __move_assignment<__traits<_Types...>> { \ using __base_type = __move_assignment<__traits<_Types...>>; \ @@ -1590,18 +1692,12 @@ __lhs.index(), __convert_to_bool>{}, __lhs, __rhs); } -template +template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { +constexpr decltype(auto) visit(_Vis&& __vis, _Vs&&... __vs) { using __variant_detail::__visitation::__variant; - bool __results[] = {__vs.valueless_by_exception()...}; - for (bool __result : __results) { - if (__result) { - __throw_bad_variant_access(); - } - } - return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor), + return __variant::__visit_value(_VSTD::forward<_Vis>(__vis), _VSTD::forward<_Vs>(__vs)...); } diff --git a/libcxx/test/libcxx/utilities/variant/variant.visit/visit_without_switch.pass.cpp b/libcxx/test/libcxx/utilities/variant/variant.visit/visit_without_switch.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/variant/variant.visit/visit_without_switch.pass.cpp @@ -0,0 +1,321 @@ +// -*- 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); + +#define _LIBCPP_DISABLE_SWITCH_VISIT_IN_VARIANT + +#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 bool operator()(Args &&...) & { + set_call(CT_NonConst | CT_LValue); + return true; + } + + template bool operator()(Args &&...) const & { + set_call(CT_Const | CT_LValue); + return true; + } + + // Don't allow the call operator to be invoked as an rvalue. + template bool operator()(Args &&...) && { + set_call(CT_NonConst | CT_RValue); + return true; + } + + template bool operator()(Args &&...) const && { + set_call(CT_Const | CT_RValue); + return true; + } + + 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))); + } +} + +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)); + } + { // multi argument - multi variant + using S = const std::string &; + using V = std::variant; + const std::string str = "hello"; + long l = 43; + V v1(42); + const V &cv1 = v1; + V v2(str); + const V &cv2 = v2; + V v3(std::move(l)); + const V &cv3 = v3; + std::visit(obj, v1, v2, v3); + assert((Fn::check_call(Val))); + std::visit(obj, cv1, cv2, std::move(v3)); + assert((Fn::check_call(Val))); + } +#endif +} + +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, ""); + } +} + +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)); + } +#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_constexpr(); + test_exceptions(); + test_caller_accepts_nonconst(); + + return 0; +}