[libcxx-commits] [libcxx] 3412bc7 - [libc++][variant] P2637R3: Member `visit` (`std::variant`) (#76447)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jan 20 21:16:55 PST 2024
Author: Hristo Hristov
Date: 2024-01-21T07:16:51+02:00
New Revision: 3412bc765887d2b2244e4338adc2f49420cce9c8
URL: https://github.com/llvm/llvm-project/commit/3412bc765887d2b2244e4338adc2f49420cce9c8
DIFF: https://github.com/llvm/llvm-project/commit/3412bc765887d2b2244e4338adc2f49420cce9c8.diff
LOG: [libc++][variant] P2637R3: Member `visit` (`std::variant`) (#76447)
Implements parts of: `P2637R3` https://wg21.link/P2637R3
(https://eel.is/c++draft/variant.visit)
Implements:
`variant.visit()`
`variant.visit<R>()`
The tests are as close as possible to the non-member function.
To land after: https://github.com/llvm/llvm-project/pull/76268
---------
Co-authored-by: Zingam <zingam at outlook.com>
Added:
libcxx/test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp
libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp
libcxx/test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp
Modified:
libcxx/docs/Status/Cxx2cPapers.csv
libcxx/include/__config
libcxx/include/variant
libcxx/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index bdfdfdd2d53c80..f6124d9a6c4e38 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -17,7 +17,7 @@
"`P0792R14 <https://wg21.link/P0792R14>`__","LWG","``function_ref``: a type-erased callable reference","Varna June 2023","","",""
"`P2874R2 <https://wg21.link/P2874R2>`__","LWG","Mandating Annex D Require No More","Varna June 2023","","",""
"`P2757R3 <https://wg21.link/P2757R3>`__","LWG","Type-checking format args","Varna June 2023","","","|format|"
-"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","","","|format|"
+"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","|Partial|","18.0",""
"`P2641R4 <https://wg21.link/P2641R4>`__","CWG, LWG","Checking if a ``union`` alternative is active","Varna June 2023","","",""
"`P1759R6 <https://wg21.link/P1759R6>`__","LWG","Native handles and file streams","Varna June 2023","|Complete|","18.0",""
"`P2697R1 <https://wg21.link/P2697R1>`__","LWG","Interfacing ``bitset`` with ``string_view``","Varna June 2023","|Complete|","18.0",""
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 90a4585938a13f..2825d03b7c2052 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1513,6 +1513,11 @@ __sanitizer_verify_double_ended_contiguous_container(const void*, const void*, c
# define _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
# endif
+// Clang-18 has support for deducing this, but it does not set the FTM.
+# if defined(__cpp_explicit_this_parameter) || (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 1800)
+# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER
+# endif
+
#endif // __cplusplus
#endif // _LIBCPP___CONFIG
diff --git a/libcxx/include/variant b/libcxx/include/variant
index 6179b2a1a0ab61..ac69645a0fab0d 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -69,6 +69,12 @@ namespace std {
// 20.7.2.6, swap
void swap(variant&) noexcept(see below);
+
+ // [variant.visit], visitation
+ template<class Self, class Visitor>
+ constexpr decltype(auto) visit(this Self&&, Visitor&&); // Since C++26
+ template<class R, class Self, class Visitor>
+ constexpr R visit(this Self&&, Visitor&&); // Since C++26
};
// 20.7.3, variant helper classes
@@ -235,6 +241,7 @@ namespace std {
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
+#include <__utility/forward_like.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <__utility/swap.h>
@@ -1130,6 +1137,19 @@ using __best_match_t = typename invoke_result_t<_MakeOverloads<_Types...>, _Tp,
} // namespace __variant_detail
+template <class _Visitor, class... _Vs, typename = void_t<decltype(std::__as_variant(std::declval<_Vs>()))...>>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr decltype(auto)
+visit(_Visitor&& __visitor, _Vs&&... __vs);
+
+# if _LIBCPP_STD_VER >= 20
+template <class _Rp,
+ class _Visitor,
+ class... _Vs,
+ typename = void_t<decltype(std::__as_variant(std::declval<_Vs>()))...>>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr _Rp
+visit(_Visitor&& __visitor, _Vs&&... __vs);
+# endif
+
template <class... _Types>
class _LIBCPP_TEMPLATE_VIS _LIBCPP_DECLSPEC_EMPTY_BASES variant
: private __sfinae_ctor_base< __all<is_copy_constructible_v<_Types>...>::value,
@@ -1273,6 +1293,27 @@ public:
__impl_.__swap(__that.__impl_);
}
+# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER)
+ // Helper class to implement [variant.visit]/10
+ // Constraints: The call to visit does not use an explicit template-argument-list
+ // that begins with a type template-argument.
+ struct __variant_visit_barrier_tag {
+ _LIBCPP_HIDE_FROM_ABI explicit __variant_visit_barrier_tag() = default;
+ };
+
+ template <__variant_visit_barrier_tag = __variant_visit_barrier_tag{}, class _Self, class _Visitor>
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) visit(this _Self&& __self, _Visitor&& __visitor) {
+ using _VariantT = _OverrideRef<_Self&&, _CopyConst<remove_reference_t<_Self>, variant>>;
+ return std::visit(std::forward<_Visitor>(__visitor), (_VariantT)__self);
+ }
+
+ template <class _Rp, class _Self, class _Visitor>
+ _LIBCPP_HIDE_FROM_ABI constexpr _Rp visit(this _Self&& __self, _Visitor&& __visitor) {
+ using _VariantT = _OverrideRef<_Self&&, _CopyConst<remove_reference_t<_Self>, variant>>;
+ return std::visit<_Rp>(std::forward<_Visitor>(__visitor), (_VariantT)__self);
+ }
+# endif
+
private:
__variant_detail::__impl<_Types...> __impl_;
@@ -1511,7 +1552,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr vo
}
}
-template < class _Visitor, class... _Vs, typename = void_t<decltype(std::__as_variant(std::declval<_Vs>()))...> >
+template < class _Visitor, class... _Vs, typename>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr decltype(auto)
visit(_Visitor&& __visitor, _Vs&&... __vs) {
using __variant_detail::__visitation::__variant;
@@ -1520,10 +1561,7 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) {
}
# if _LIBCPP_STD_VER >= 20
-template < class _Rp,
- class _Visitor,
- class... _Vs,
- typename = void_t<decltype(std::__as_variant(std::declval<_Vs>()))...> >
+template < class _Rp, class _Visitor, class... _Vs, typename>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr _Rp
visit(_Visitor&& __visitor, _Vs&&... __vs) {
using __variant_detail::__visitation::__variant;
diff --git a/libcxx/test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp
new file mode 100644
index 00000000000000..c54f2b722d46a9
--- /dev/null
+++ b/libcxx/test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20, c++23
+// The tested functionality needs deducing this.
+// UNSUPPORTED: clang-16 || clang-17
+// XFAIL: apple-clang
+
+// <variant>
+
+// class variant;
+// template<class Self, class Visitor>
+// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
+// template<class R, class Self, class Visitor>
+// constexpr R visit(this Self&&, Visitor&&); // since C++26
+
+#include <variant>
+
+#include "test_macros.h"
+
+struct Incomplete;
+template <class T>
+struct Holder {
+ T t;
+};
+
+constexpr bool test(bool do_it) {
+ if (do_it) {
+ std::variant<Holder<Incomplete>*, int> v = nullptr;
+
+ v.visit([](auto) {});
+ v.visit([](auto) -> Holder<Incomplete>* { return nullptr; });
+ v.visit<void>([](auto) {});
+ v.visit<void*>([](auto) -> Holder<Incomplete>* { return nullptr; });
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test(true);
+ static_assert(test(true));
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp
new file mode 100644
index 00000000000000..68706d6c32af4f
--- /dev/null
+++ b/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp
@@ -0,0 +1,258 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20, c++23
+// The tested functionality needs deducing this.
+// UNSUPPORTED: clang-16 || clang-17
+// XFAIL: apple-clang
+
+// <variant>
+
+// class variant;
+
+// template<class Self, class Visitor>
+// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include "test_macros.h"
+#include "variant_test_helpers.h"
+
+void test_call_operator_forwarding() {
+ using Fn = ForwardingCallObject;
+ Fn obj{};
+ const Fn& cobj = obj;
+
+ { // test call operator forwarding - single variant, single arg
+ using V = std::variant<int>;
+ V v(42);
+
+ v.visit(obj);
+ assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
+ v.visit(cobj);
+ assert(Fn::check_call<int&>(CT_Const | CT_LValue));
+ v.visit(std::move(obj));
+ assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
+ v.visit(std::move(cobj));
+ 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);
+
+ v.visit(obj);
+ assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
+ v.visit(cobj);
+ assert(Fn::check_call<long&>(CT_Const | CT_LValue));
+ v.visit(std::move(obj));
+ assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
+ v.visit(std::move(cobj));
+ assert(Fn::check_call<long&>(CT_Const | CT_RValue));
+ }
+}
+
+// Applies to non-member `std::visit` only.
+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;
+
+ v.visit(obj);
+ assert(Fn::check_call<int&>(val));
+ cv.visit(obj);
+ assert(Fn::check_call<const int&>(val));
+ std::move(v).visit(obj);
+ assert(Fn::check_call<int&&>(val));
+ std::move(cv).visit(obj);
+ 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;
+
+ v.visit(obj);
+ assert(Fn::check_call<int&>(val));
+ cv.visit(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(v).visit(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(cv).visit(obj);
+ assert(Fn::check_call<int&>(val));
+ assert(false);
+ }
+ { // single argument - rvalue reference
+ using V = std::variant<int&&>;
+ int x = 42;
+ V v(std::move(x));
+ const V& cv = v;
+
+ v.visit(obj);
+ assert(Fn::check_call<int&>(val));
+ cvstd::visit(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(v).visit(obj);
+ assert(Fn::check_call<int&&>(val));
+ std::move(cv).visit(obj);
+ assert(Fn::check_call<int&&>(val));
+ }
+#endif
+}
+
+void test_return_type() {
+ using Fn = ForwardingCallObject;
+ Fn obj{};
+ const Fn& cobj = obj;
+
+ { // test call operator forwarding - single variant, single arg
+ using V = std::variant<int>;
+ V v(42);
+
+ static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
+ static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
+ static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
+ static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
+ }
+ { // 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(v.visit(obj)), Fn&>);
+ static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
+ static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
+ static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
+ }
+}
+
+void test_constexpr() {
+ constexpr ReturnFirst obj{};
+
+ {
+ using V = std::variant<int>;
+ constexpr V v(42);
+
+ static_assert(v.visit(obj) == 42);
+ }
+ {
+ using V = std::variant<short, long, char>;
+ constexpr V v(42L);
+
+ static_assert(v.visit(obj) == 42);
+ }
+}
+
+void test_exceptions() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ ReturnArity obj{};
+
+ auto test = [&](auto&& v) {
+ try {
+ v.visit(obj);
+ } catch (const std::bad_variant_access&) {
+ return true;
+ } catch (...) {
+ }
+ return false;
+ };
+
+ {
+ using V = std::variant<int, MakeEmptyT>;
+ V v;
+ makeEmpty(v);
+
+ assert(test(v));
+ }
+#endif
+}
+
+// See https://llvm.org/PR31916
+void test_caller_accepts_nonconst() {
+ struct A {};
+ struct Visitor {
+ void operator()(A&) {}
+ };
+ std::variant<A> v;
+
+ v.visit(Visitor{});
+}
+
+struct MyVariant : std::variant<short, long, float> {};
+
+namespace std {
+template <std::size_t Index>
+void get(const MyVariant&) {
+ assert(false);
+}
+} // namespace std
+
+void test_derived_from_variant() {
+ auto v1 = MyVariant{42};
+ const auto cv1 = MyVariant{142};
+
+ v1.visit([](auto x) { assert(x == 42); });
+ cv1.visit([](auto x) { assert(x == 142); });
+ MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); });
+ std::move(v1).visit([](auto x) { assert(x == 42); });
+ std::move(cv1).visit([](auto x) { assert(x == 142); });
+
+ // Check that visit does not take index nor valueless_by_exception members from the base class.
+ struct EvilVariantBase {
+ int index;
+ char valueless_by_exception;
+ };
+
+ struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {
+ using std::variant<int, long, double>::variant;
+ };
+
+ EvilVariant1{12}.visit([](auto x) { assert(x == 12); });
+ EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); });
+
+ // Check that visit unambiguously picks the variant, even if the other base has __impl member.
+ struct ImplVariantBase {
+ struct Callable {
+ bool operator()() const {
+ assert(false);
+ return false;
+ }
+ };
+
+ Callable __impl;
+ };
+
+ struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
+ using std::variant<int, long, double>::variant;
+ };
+
+ EvilVariant2{12}.visit([](auto x) { assert(x == 12); });
+ EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); });
+}
+
+int main(int, char**) {
+ test_call_operator_forwarding();
+ test_argument_forwarding();
+ test_return_type();
+ test_constexpr();
+ test_exceptions();
+ test_caller_accepts_nonconst();
+ test_derived_from_variant();
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp
new file mode 100644
index 00000000000000..20472c62fc5f98
--- /dev/null
+++ b/libcxx/test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp
@@ -0,0 +1,345 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20, c++23
+// The tested functionality needs deducing this.
+// UNSUPPORTED: clang-16 || clang-17
+// XFAIL: apple-clang
+
+// <variant>
+
+// class variant;
+
+// template<class R, class Self, class Visitor>
+// constexpr R visit(this Self&&, Visitor&&); // since C++26
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include "test_macros.h"
+#include "variant_test_helpers.h"
+
+template <class... Ts>
+struct overloaded : Ts... {
+ using Ts::operator()...;
+};
+
+void test_overload_ambiguity() {
+ using V = std::variant<float, long, std::string>;
+ using namespace std::string_literals;
+ V v{"baba"s};
+
+ v.visit(
+ overloaded{[]([[maybe_unused]] auto x) { assert(false); }, [](const std::string& x) { assert(x == "baba"s); }});
+ assert(std::get<std::string>(v) == "baba"s);
+
+ // Test the constraint.
+ v = std::move(v).visit<V>(overloaded{
+ []([[maybe_unused]] auto x) {
+ assert(false);
+ return 0;
+ },
+ [](const std::string& x) {
+ assert(x == "baba"s);
+ return x + " zmt"s;
+ }});
+ assert(std::get<std::string>(v) == "baba zmt"s);
+}
+
+template <typename ReturnType>
+void test_call_operator_forwarding() {
+ using Fn = ForwardingCallObject;
+ Fn obj{};
+ const Fn& cobj = obj;
+
+ { // test call operator forwarding - single variant, single arg
+ using V = std::variant<int>;
+ V v(42);
+
+ v.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
+ v.visit<ReturnType>(cobj);
+ assert(Fn::check_call<int&>(CT_Const | CT_LValue));
+ v.visit<ReturnType>(std::move(obj));
+ assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
+ v.visit<ReturnType>(std::move(cobj));
+ 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);
+
+ v.visit<ReturnType>(obj);
+ assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
+ v.visit<ReturnType>(cobj);
+ assert(Fn::check_call<long&>(CT_Const | CT_LValue));
+ v.visit<ReturnType>(std::move(obj));
+ assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
+ v.visit<ReturnType>(std::move(cobj));
+ assert(Fn::check_call<long&>(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;
+
+ v.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ cv.visit<ReturnType>(obj);
+ assert(Fn::check_call<const int&>(val));
+ std::move(v).visit<ReturnType>(obj);
+ assert(Fn::check_call<int&&>(val));
+ std::move(cv).visit<ReturnType>(obj);
+ 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;
+
+ v.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ cv.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(v).visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(cv).visit<ReturnType>(obj);
+ 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;
+
+ v.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ cv.visit<ReturnType>(obj);
+ assert(Fn::check_call<int&>(val));
+ std::move(v).visit<ReturnType>(obj);
+ assert(Fn::check_call<int&&>(val));
+ std::move(cv).visit<ReturnType>(obj);
+ assert(Fn::check_call<int&&>(val));
+ }
+#endif
+}
+
+template <typename ReturnType>
+void test_return_type() {
+ using Fn = ForwardingCallObject;
+ Fn obj{};
+ const Fn& cobj = obj;
+
+ { // test call operator forwarding - no variant
+ // non-member
+ {
+ 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(v.visit<ReturnType>(obj)), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(cobj)), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(std::move(obj))), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(std::move(cobj))), 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(v.visit<ReturnType>(obj)), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(cobj)), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(std::move(obj))), ReturnType>);
+ static_assert(std::is_same_v<decltype(v.visit<ReturnType>(std::move(cobj))), ReturnType>);
+ }
+}
+
+void test_constexpr_void() {
+ constexpr ReturnFirst obj{};
+
+ {
+ using V = std::variant<int>;
+ constexpr V v(42);
+
+ static_assert((v.visit<void>(obj), 42) == 42);
+ }
+ {
+ using V = std::variant<short, long, char>;
+ constexpr V v(42L);
+
+ static_assert((v.visit<void>(obj), 42) == 42);
+ }
+}
+
+void test_constexpr_int() {
+ constexpr ReturnFirst obj{};
+
+ {
+ using V = std::variant<int>;
+ constexpr V v(42);
+
+ static_assert(v.visit<int>(obj) == 42);
+ }
+ {
+ using V = std::variant<short, long, char>;
+ constexpr V v(42L);
+
+ static_assert(v.visit<int>(obj) == 42);
+ }
+}
+
+template <typename ReturnType>
+void test_exceptions() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ ReturnArity obj{};
+
+ auto test = [&](auto&& v) {
+ try {
+ v.template visit<ReturnType>(obj);
+ } catch (const std::bad_variant_access&) {
+ return true;
+ } catch (...) {
+ }
+ return false;
+ };
+
+ {
+ using V = std::variant<int, MakeEmptyT>;
+ V v;
+ makeEmpty(v);
+
+ assert(test(v));
+ }
+#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;
+
+ v.template visit<ReturnType>(Visitor{});
+}
+
+void test_constexpr_explicit_side_effect() {
+ auto test_lambda = [](int arg) constexpr {
+ std::variant<int> v = 101;
+
+ {
+ v.template visit<void>([arg](int& x) constexpr { x = arg; });
+ }
+
+ return std::get<int>(v);
+ };
+
+ static_assert(test_lambda(202) == 202);
+}
+
+void test_derived_from_variant() {
+ struct MyVariant : std::variant<short, long, float> {};
+
+ MyVariant{42}.template visit<bool>([](auto x) {
+ assert(x == 42);
+ return true;
+ });
+ MyVariant{-1.3f}.template visit<bool>([](auto x) {
+ assert(x == -1.3f);
+ return true;
+ });
+
+ // Check that visit does not take index nor valueless_by_exception members from the base class.
+ struct EvilVariantBase {
+ int index;
+ char valueless_by_exception;
+ };
+
+ struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {
+ using std::variant<int, long, double>::variant;
+ };
+
+ EvilVariant1{12}.template visit<bool>([](auto x) {
+ assert(x == 12);
+ return true;
+ });
+ EvilVariant1{12.3}.template visit<bool>([](auto x) {
+ assert(x == 12.3);
+ return true;
+ });
+
+ // Check that visit unambiguously picks the variant, even if the other base has __impl member.
+ struct ImplVariantBase {
+ struct Callable {
+ bool operator()() const {
+ assert(false);
+ return false;
+ }
+ };
+
+ Callable __impl;
+ };
+
+ struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
+ using std::variant<int, long, double>::variant;
+ };
+
+ EvilVariant2{12}.template visit<bool>([](auto x) {
+ assert(x == 12);
+ return true;
+ });
+ EvilVariant2{12.3}.template visit<bool>([](auto x) {
+ assert(x == 12.3);
+ return true;
+ });
+}
+
+int main(int, char**) {
+ test_overload_ambiguity();
+ 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();
+ test_derived_from_variant();
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 958c34edde0059..a5df187b046f66 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1267,7 +1267,11 @@ def add_version_header(tc):
},
{
"name": "__cpp_lib_variant",
- "values": {"c++17": 202102},
+ "values": {
+ "c++17": 202102, # std::visit for classes derived from std::variant
+ # "c++20": 202106, # Fully constexpr std::variant
+ # "c++26": 202306, # Member visit (implemented)
+ },
"headers": ["variant"],
},
{
More information about the libcxx-commits
mailing list