diff options
author | Ruslan Arutyunyan <ruslan.arutyunyan@intel.com> | 2021-01-25 11:12:25 -0500 |
---|---|---|
committer | Louis Dionne <ldionne.2@gmail.com> | 2021-01-25 11:14:45 -0500 |
commit | 51faba35fd81fbd3af407a29c136895a718ccd96 (patch) | |
tree | d1116f16bb7a57eab7d501d638ea12bb9f1cffa4 /libcxx | |
parent | 95f0d1edafe3e52a4057768f8cde5d55faf39d16 (diff) | |
download | llvm-51faba35fd81fbd3af407a29c136895a718ccd96.zip llvm-51faba35fd81fbd3af407a29c136895a718ccd96.tar.gz llvm-51faba35fd81fbd3af407a29c136895a718ccd96.tar.bz2 |
[libc++] Implement P0655R1 visit<R>: Explicit Return Type for visit
Differential Revision: https://reviews.llvm.org/D92044
Diffstat (limited to 'libcxx')
6 files changed, 583 insertions, 81 deletions
diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv index efdcb1b..8991a03 100644 --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -59,7 +59,7 @@ "`P0595R2 <https://wg21.link/P0595R2>`__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0" "`P0602R4 <https://wg21.link/P0602R4>`__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0" "`P0608R3 <https://wg21.link/P0608R3>`__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0" -"`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","* *","" +"`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","|Complete|","12.0" "`P0771R1 <https://wg21.link/P0771R1>`__","LWG","std::function move constructor should be noexcept","San Diego","|Complete|","6.0" "`P0896R4 <https://wg21.link/P0896R4>`__","LWG","The One Ranges Proposal","San Diego","* *","" "`P0899R1 <https://wg21.link/P0899R1>`__","LWG","P0899R1 - LWG 3016 is not a defect","San Diego","|Nothing To Do|","" diff --git a/libcxx/include/variant b/libcxx/include/variant index daa3dd2..cb46f09 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -169,6 +169,9 @@ namespace std { template <class Visitor, class... Variants> constexpr see below visit(Visitor&&, Variants&&...); + template <class R, class Visitor, class... Variants> + constexpr R visit(Visitor&&, Variants&&...); // since C++20 + // 20.7.7, class monostate struct monostate; @@ -583,6 +586,16 @@ struct __variant { __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), _VSTD::forward<_Vs>(__vs)...); } +#if _LIBCPP_STD_VER > 17 + template <class _Rp, class _Visitor, class... _Vs> + 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 <class _Visitor, class... _Values> @@ -605,12 +618,43 @@ private: _Visitor&& __visitor; }; +#if _LIBCPP_STD_VER > 17 + template <class _Rp, class _Visitor> + struct __value_visitor_return_type { + template <class... _Alts> + 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 <class _Visitor> 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 <class _Rp, class _Visitor> + 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 @@ constexpr bool operator>=(const variant<_Types...>& __lhs, template <class _Visitor, class... _Vs> 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 <class _Visitor, class... _Vs> +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 <class _Rp, class _Visitor, class... _Vs> +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 index af3cd75..94ca6cf 100644 --- 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 @@ constexpr bool test(bool do_it) std::variant<Holder<Incomplete>*, int> v = nullptr; std::visit([](auto){}, v); std::visit([](auto) -> Holder<Incomplete>* { return nullptr; }, v); +#if TEST_STD_VER > 17 + std::visit<void>([](auto){}, v); + std::visit<void*>([](auto) -> Holder<Incomplete>* { 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 index 0edb83b..b88ecc0 100644 --- 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 <variant> #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<CallType>(static_cast<unsigned>(LHS) | - static_cast<unsigned>(RHS)); -} - -struct ForwardingCallObject { - - template <class... Args> - ForwardingCallObject& operator()(Args&&...) & { - set_call<Args &&...>(CT_NonConst | CT_LValue); - return *this; - } - - template <class... Args> - const ForwardingCallObject& operator()(Args&&...) const & { - set_call<Args &&...>(CT_Const | CT_LValue); - return *this; - } - - template <class... Args> - ForwardingCallObject&& operator()(Args&&...) && { - set_call<Args &&...>(CT_NonConst | CT_RValue); - return std::move(*this); - } - - template <class... Args> - const ForwardingCallObject&& operator()(Args&&...) const && { - set_call<Args &&...>(CT_Const | CT_RValue); - return std::move(*this); - } - - template <class... Args> 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<Args...>()); - } - - template <class... Args> static bool check_call(CallType type) { - bool result = last_call_type == type && last_call_args && - *last_call_args == makeArgumentID<Args...>(); - 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 @@ void test_return_type() { } } -struct ReturnFirst { - template <class... Args> constexpr int operator()(int f, Args &&...) const { - return f; - } -}; - -struct ReturnArity { - template <class... Args> 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_return_type.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp new file mode 100644 index 0000000..7e569e2 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp @@ -0,0 +1,430 @@ +// -*- 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, c++17 + +// 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 + +// <variant> +// template <class R, class Visitor, class... Variants> +// constexpr R visit(Visitor&& vis, Variants&&... vars); + +#include <cassert> +#include <memory> +#include <string> +#include <type_traits> +#include <utility> +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.h" + +template <typename ReturnType> +void test_call_operator_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn &cobj = obj; + { // test call operator forwarding - no variant + std::visit<ReturnType>(obj); + assert(Fn::check_call<>(CT_NonConst | CT_LValue)); + std::visit<ReturnType>(cobj); + assert(Fn::check_call<>(CT_Const | CT_LValue)); + std::visit<ReturnType>(std::move(obj)); + assert(Fn::check_call<>(CT_NonConst | CT_RValue)); + std::visit<ReturnType>(std::move(cobj)); + assert(Fn::check_call<>(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, single arg + using V = std::variant<int>; + V v(42); + std::visit<ReturnType>(obj, v); + assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); + std::visit<ReturnType>(cobj, v); + assert(Fn::check_call<int &>(CT_Const | CT_LValue)); + std::visit<ReturnType>(std::move(obj), v); + assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); + std::visit<ReturnType>(std::move(cobj), v); + assert(Fn::check_call<int &>(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant<int, long, double>; + V v(42l); + std::visit<ReturnType>(obj, v); + assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); + std::visit<ReturnType>(cobj, v); + assert(Fn::check_call<long &>(CT_Const | CT_LValue)); + std::visit<ReturnType>(std::move(obj), v); + assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); + std::visit<ReturnType>(std::move(cobj), v); + assert(Fn::check_call<long &>(CT_Const | CT_RValue)); + } + { // test call operator forwarding - multi variant, multi arg + using V = std::variant<int, long, double>; + using V2 = std::variant<int *, std::string>; + V v(42l); + V2 v2("hello"); + std::visit<int>(obj, v, v2); + assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); + std::visit<ReturnType>(cobj, v, v2); + assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); + std::visit<ReturnType>(std::move(obj), v, v2); + assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); + std::visit<ReturnType>(std::move(cobj), v, v2); + assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); + } + { + using V = std::variant<int, long, double, std::string>; + V v1(42l), v2("hello"), v3(101), v4(1.1); + std::visit<ReturnType>(obj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue))); + std::visit<ReturnType>(cobj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue))); + std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue))); + std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue))); + } + { + using V = std::variant<int, long, double, int*, std::string>; + V v1(42l), v2("hello"), v3(nullptr), v4(1.1); + std::visit<ReturnType>(obj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue))); + std::visit<ReturnType>(cobj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue))); + std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue))); + std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue))); + } +} + +template <typename ReturnType> +void test_argument_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const auto Val = CT_LValue | CT_NonConst; + { // single argument - value type + using V = std::variant<int>; + V v(42); + const V &cv = v; + std::visit<ReturnType>(obj, v); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, cv); + assert(Fn::check_call<const int &>(Val)); + std::visit<ReturnType>(obj, std::move(v)); + assert(Fn::check_call<int &&>(Val)); + std::visit<ReturnType>(obj, std::move(cv)); + assert(Fn::check_call<const int &&>(Val)); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { // single argument - lvalue reference + using V = std::variant<int &>; + int x = 42; + V v(x); + const V &cv = v; + std::visit<ReturnType>(obj, v); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, cv); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, std::move(v)); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, std::move(cv)); + assert(Fn::check_call<int &>(Val)); + } + { // single argument - rvalue reference + using V = std::variant<int &&>; + int x = 42; + V v(std::move(x)); + const V &cv = v; + std::visit<ReturnType>(obj, v); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, cv); + assert(Fn::check_call<int &>(Val)); + std::visit<ReturnType>(obj, std::move(v)); + assert(Fn::check_call<int &&>(Val)); + std::visit<ReturnType>(obj, std::move(cv)); + assert(Fn::check_call<int &&>(Val)); + } +#endif + { // multi argument - multi variant + using V = std::variant<int, std::string, long>; + V v1(42), v2("hello"), v3(43l); + std::visit<ReturnType>(obj, v1, v2, v3); + assert((Fn::check_call<int &, std::string &, long &>(Val))); + std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3)); + assert((Fn::check_call<const int &, const std::string &, long &&>(Val))); + } + { + using V = std::variant<int, long, double, std::string>; + V v1(42l), v2("hello"), v3(101), v4(1.1); + std::visit<ReturnType>(obj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int &, double &>(Val))); + std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); + assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val))); + } + { + using V = std::variant<int, long, double, int*, std::string>; + V v1(42l), v2("hello"), v3(nullptr), v4(1.1); + std::visit<ReturnType>(obj, v1, v2, v3, v4); + assert((Fn::check_call<long &, std::string &, int *&, double &>(Val))); + std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4)); + assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val))); + } +} + +template <typename ReturnType> +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<decltype(std::visit<ReturnType>(obj)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj))), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj))), ReturnType>); + } + { // test call operator forwarding - single variant, single arg + using V = std::variant<int>; + V v(42); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v)), ReturnType>); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant<int, long, double>; + V v(42l); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v)), ReturnType>); + } + { // test call operator forwarding - multi variant, multi arg + using V = std::variant<int, long, double>; + using V2 = std::variant<int *, std::string>; + V v(42l); + V2 v2("hello"); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v, v2)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v, v2)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v, v2)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v, v2)), ReturnType>); + } + { + using V = std::variant<int, long, double, std::string>; + V v1(42l), v2("hello"), v3(101), v4(1.1); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4)), ReturnType>); + } + { + using V = std::variant<int, long, double, int*, std::string>; + V v1(42l), v2("hello"), v3(nullptr), v4(1.1); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4)), ReturnType>); + static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4)), ReturnType>); + } +} + +void test_constexpr_void() { + constexpr ReturnFirst obj{}; + constexpr ReturnArity aobj{}; + { + using V = std::variant<int>; + constexpr V v(42); + static_assert((std::visit<void>(obj, v), 42) == 42, ""); + } + { + using V = std::variant<short, long, char>; + constexpr V v(42l); + static_assert((std::visit<void>(obj, v), 42) == 42, ""); + } + { + using V1 = std::variant<int>; + using V2 = std::variant<int, char *, long long>; + using V3 = std::variant<bool, int, int>; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert((std::visit<void>(aobj, v1, v2, v3), 3) == 3, ""); + } + { + using V1 = std::variant<int>; + using V2 = std::variant<int, char *, long long>; + using V3 = std::variant<void *, int, int>; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert((std::visit<void>(aobj, v1, v2, v3), 3) == 3, ""); + } + { + using V = std::variant<int, long, double, int *>; + constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); + static_assert((std::visit<void>(aobj, v1, v2, v3, v4), 4) == 4, ""); + } + { + using V = std::variant<int, long, double, long long, int *>; + constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); + static_assert((std::visit<void>(aobj, v1, v2, v3, v4), 4) == 4, ""); + } +} + +void test_constexpr_int() { + constexpr ReturnFirst obj{}; + constexpr ReturnArity aobj{}; + { + using V = std::variant<int>; + constexpr V v(42); + static_assert(std::visit<int>(obj, v) == 42, ""); + } + { + using V = std::variant<short, long, char>; + constexpr V v(42l); + static_assert(std::visit<int>(obj, v) == 42, ""); + } + { + using V1 = std::variant<int>; + using V2 = std::variant<int, char *, long long>; + using V3 = std::variant<bool, int, int>; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert(std::visit<int>(aobj, v1, v2, v3) == 3, ""); + } + { + using V1 = std::variant<int>; + using V2 = std::variant<int, char *, long long>; + using V3 = std::variant<void *, int, int>; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert(std::visit<int>(aobj, v1, v2, v3) == 3, ""); + } + { + using V = std::variant<int, long, double, int *>; + constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); + static_assert(std::visit<int>(aobj, v1, v2, v3, v4) == 4, ""); + } + { + using V = std::variant<int, long, double, long long, int *>; + constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1); + static_assert(std::visit<int>(aobj, v1, v2, v3, v4) == 4, ""); + } +} + +template <typename ReturnType> +void test_exceptions() { +#ifndef TEST_HAS_NO_EXCEPTIONS + ReturnArity obj{}; + auto test = [&](auto &&... args) { + try { + std::visit<ReturnType>(obj, args...); + } catch (const std::bad_variant_access &) { + return true; + } catch (...) { + } + return false; + }; + { + using V = std::variant<int, MakeEmptyT>; + V v; + makeEmpty(v); + assert(test(v)); + } + { + using V = std::variant<int, MakeEmptyT>; + using V2 = std::variant<long, std::string, void *>; + V v; + makeEmpty(v); + V2 v2("hello"); + assert(test(v, v2)); + } + { + using V = std::variant<int, MakeEmptyT>; + using V2 = std::variant<long, std::string, void *>; + V v; + makeEmpty(v); + V2 v2("hello"); + assert(test(v2, v)); + } + { + using V = std::variant<int, MakeEmptyT>; + using V2 = std::variant<long, std::string, void *, MakeEmptyT>; + V v; + makeEmpty(v); + V2 v2; + makeEmpty(v2); + assert(test(v, v2)); + } + { + using V = std::variant<int, long, double, MakeEmptyT>; + V v1(42l), v2(101), v3(202), v4(1.1); + makeEmpty(v1); + assert(test(v1, v2, v3, v4)); + } + { + using V = std::variant<int, long, double, long long, MakeEmptyT>; + 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 +template <typename ReturnType> +void test_caller_accepts_nonconst() { + struct A {}; + struct Visitor { + auto operator()(A&) { + if constexpr (!std::is_void_v<ReturnType>) + { + return ReturnType{}; + } + } + }; + std::variant<A> v; + std::visit<ReturnType>(Visitor{}, v); +} + +void test_constexpr_explicit_side_effect() { + auto test_lambda = [](int arg) constexpr { + std::variant<int> v = 101; + std::visit<void>([arg](int& x) constexpr { x = arg; }, v); + return std::get<int>(v); + }; + + static_assert(test_lambda(202) == 202, ""); +} + +int main(int, char**) { + test_call_operator_forwarding<void>(); + test_argument_forwarding<void>(); + test_return_type<void>(); + test_constexpr_void(); + test_exceptions<void>(); + test_caller_accepts_nonconst<void>(); + test_call_operator_forwarding<int>(); + test_argument_forwarding<int>(); + test_return_type<int>(); + test_constexpr_int(); + test_exceptions<int>(); + test_caller_accepts_nonconst<int>(); + 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 index 0e04093..92faf79 100644 --- a/libcxx/test/support/variant_test_helpers.h +++ b/libcxx/test/support/variant_test_helpers.h @@ -14,6 +14,7 @@ #include <cassert> #include "test_macros.h" +#include "type_id.h" #if TEST_STD_VER <= 14 #error This file requires C++17 @@ -85,5 +86,83 @@ void makeEmpty(Variant& v) { } #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<CallType>(static_cast<unsigned>(LHS) | + static_cast<unsigned>(RHS)); +} + +struct ForwardingCallObject { + + template <class... Args> + ForwardingCallObject& operator()(Args&&...) & { + set_call<Args &&...>(CT_NonConst | CT_LValue); + return *this; + } + + template <class... Args> + const ForwardingCallObject& operator()(Args&&...) const & { + set_call<Args &&...>(CT_Const | CT_LValue); + return *this; + } + + template <class... Args> + ForwardingCallObject&& operator()(Args&&...) && { + set_call<Args &&...>(CT_NonConst | CT_RValue); + return std::move(*this); + } + + template <class... Args> + const ForwardingCallObject&& operator()(Args&&...) const && { + set_call<Args &&...>(CT_Const | CT_RValue); + return std::move(*this); + } + + template <class... Args> 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<Args...>()); + } + + template <class... Args> static bool check_call(CallType type) { + bool result = last_call_type == type && last_call_args && + *last_call_args == makeArgumentID<Args...>(); + last_call_type = CT_None; + last_call_args = nullptr; + return result; + } + + // To check explicit return type for visit<R> + 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 <class... Args> constexpr int operator()(int f, Args &&...) const { + return f; + } +}; + +struct ReturnArity { + template <class... Args> constexpr int operator()(Args &&...) const { + return sizeof...(Args); + } +}; #endif // SUPPORT_VARIANT_TEST_HELPERS_H |