diff --git a/libcxx/docs/Cxx2bStatusPaperStatus.csv b/libcxx/docs/Cxx2bStatusPaperStatus.csv index 79b5db07693751..9cbd990b0bb94b 100644 --- a/libcxx/docs/Cxx2bStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2bStatusPaperStatus.csv @@ -7,7 +7,7 @@ "`P1682R3 `__","LWG","std::to_underlying for enumerations","February 2021","|Complete|","13.0" "`P2017R1 `__","LWG","Conditionally borrowed ranges","February 2021","","" "`P2160R1 `__","LWG","Locks lock lockables","February 2021","","" -"`P2162R2 `__","LWG","Inheriting from std::variant","February 2021","","" +"`P2162R2 `__","LWG","Inheriting from std::variant","February 2021","|Complete|","13.0" "`P2212R2 `__","LWG","Relax Requirements for time_point::clock","February 2021","","" "`P2259R1 `__","LWG","Repairing input range adaptors and counted_iterator","February 2021","","" "","","","","","" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 40a6bdaf3a4bf1..806b30f22d74f2 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -162,7 +162,7 @@ Status ------------------------------------------------- ----------------- ``__cpp_lib_unordered_map_try_emplace`` ``201411L`` ------------------------------------------------- ----------------- - ``__cpp_lib_variant`` ``201606L`` + ``__cpp_lib_variant`` ``202102L`` ------------------------------------------------- ----------------- ``__cpp_lib_void_t`` ``201411L`` ------------------------------------------------- ----------------- diff --git a/libcxx/include/variant b/libcxx/include/variant index 770dd335bae21e..4e3db1f7afbc7b 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -318,6 +318,33 @@ using __variant_index_t = template constexpr _IndexType __variant_npos = static_cast<_IndexType>(-1); +template +class _LIBCPP_TEMPLATE_VIS variant; + +template +_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>& +__as_variant(variant<_Types...>& __vs) noexcept { + return __vs; +} + +template +_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>& +__as_variant(const variant<_Types...>& __vs) noexcept { + return __vs; +} + +template +_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&& +__as_variant(variant<_Types...>&& __vs) noexcept { + return _VSTD::move(__vs); +} + +template +_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>&& +__as_variant(const variant<_Types...>&& __vs) noexcept { + return _VSTD::move(__vs); +} + namespace __find_detail { template @@ -564,8 +591,9 @@ struct __variant { inline _LIBCPP_INLINE_VISIBILITY static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor, _Vs&&... __vs) { - return __base::__visit_alt(_VSTD::forward<_Visitor>(__visitor), - _VSTD::forward<_Vs>(__vs).__impl...); + return __base::__visit_alt( + _VSTD::forward<_Visitor>(__visitor), + _VSTD::__as_variant(_VSTD::forward<_Vs>(__vs)).__impl...); } template @@ -586,6 +614,7 @@ struct __variant { __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)), _VSTD::forward<_Vs>(__vs)...); } + #if _LIBCPP_STD_VER > 17 template inline _LIBCPP_INLINE_VISIBILITY @@ -1637,18 +1666,21 @@ constexpr bool operator>=(const variant<_Types...>& __lhs, template inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr void __throw_if_valueless(_Vs&&... __vs) { - const bool __valueless = (... || __vs.valueless_by_exception()); + _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr void + __throw_if_valueless(_Vs&&... __vs) { + const bool __valueless = + (... || _VSTD::__as_variant(__vs).valueless_by_exception()); if (__valueless) { - __throw_bad_variant_access(); + __throw_bad_variant_access(); } } -template +template < + class _Visitor, class... _Vs, + typename = void_t()))...> > inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { + _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<_Vs>(__vs)...); return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor), @@ -1656,10 +1688,12 @@ constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) { } #if _LIBCPP_STD_VER > 17 -template +template < + class _Rp, class _Visitor, class... _Vs, + typename = void_t()))...> > inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS -constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) { + _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr _Rp + visit(_Visitor&& __visitor, _Vs&&... __vs) { using __variant_detail::__visitation::__variant; _VSTD::__throw_if_valueless(_VSTD::forward<_Vs>(__vs)...); return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor), diff --git a/libcxx/include/version b/libcxx/include/version index 040d72e1254b6b..7ff5f914d76caf 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -160,7 +160,7 @@ __cpp_lib_type_trait_variable_templates 201510L __cpp_lib_uncaught_exceptions 201411L __cpp_lib_unordered_map_try_emplace 201411L __cpp_lib_unwrap_ref 201811L -__cpp_lib_variant 201606L +__cpp_lib_variant 202102L __cpp_lib_void_t 201411L */ @@ -259,7 +259,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_type_trait_variable_templates 201510L # define __cpp_lib_uncaught_exceptions 201411L # define __cpp_lib_unordered_map_try_emplace 201411L -# define __cpp_lib_variant 201606L +# define __cpp_lib_variant 202102L # define __cpp_lib_void_t 201411L #endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp index 6d198d1121f744..f34c68183c151f 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp @@ -16,7 +16,7 @@ // Test the feature test macros defined by /* Constant Value - __cpp_lib_variant 201606L [C++17] + __cpp_lib_variant 202102L [C++17] */ #include @@ -39,8 +39,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++17" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++17" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++17" # endif #elif TEST_STD_VER == 20 @@ -48,8 +48,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++20" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++20" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++20" # endif #elif TEST_STD_VER > 20 @@ -57,8 +57,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++2b" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++2b" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++2b" # endif #endif // TEST_STD_VER > 20 diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp index 83b286b056303c..593d63d3406cbe 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -150,7 +150,7 @@ __cpp_lib_uncaught_exceptions 201411L [C++17] __cpp_lib_unordered_map_try_emplace 201411L [C++17] __cpp_lib_unwrap_ref 201811L [C++20] - __cpp_lib_variant 201606L [C++17] + __cpp_lib_variant 202102L [C++17] __cpp_lib_void_t 201411L [C++17] */ @@ -2090,8 +2090,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++17" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++17" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++17" # endif # ifndef __cpp_lib_void_t @@ -3277,8 +3277,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++20" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++20" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++20" # endif # ifndef __cpp_lib_void_t @@ -4491,8 +4491,8 @@ # ifndef __cpp_lib_variant # error "__cpp_lib_variant should be defined in c++2b" # endif -# if __cpp_lib_variant != 201606L -# error "__cpp_lib_variant should have the value 201606L in c++2b" +# if __cpp_lib_variant != 202102L +# error "__cpp_lib_variant should have the value 202102L in c++2b" # endif # ifndef __cpp_lib_void_t 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 676c5fc1f05b32..c3cc7eb77d0bba 100644 --- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp @@ -348,6 +348,86 @@ void test_caller_accepts_nonconst() { std::visit(Visitor{}, v); } +struct MyVariant : std::variant {}; + +namespace std { +template +void get(const MyVariant&) { + assert(false); +} +} // namespace std + +void test_derived_from_variant() { + auto v1 = MyVariant{42}; + const auto cv1 = MyVariant{142}; + std::visit([](auto x) { assert(x == 42); }, v1); + std::visit([](auto x) { assert(x == 142); }, cv1); + std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f}); + std::visit([](auto x) { assert(x == 42); }, std::move(v1)); + std::visit([](auto x) { assert(x == 142); }, std::move(cv1)); + + // 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, + std::tuple, + EvilVariantBase { + using std::variant::variant; + }; + + std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12}); + std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3}); + + // Check that visit unambiguously picks the variant, even if the other base has __impl member. + struct ImplVariantBase { + struct Callable { + bool operator()(); + }; + + Callable __impl; + }; + + struct EvilVariant2 : std::variant, ImplVariantBase { + using std::variant::variant; + }; + + std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12}); + std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3}); +} + +struct any_visitor { + template + void operator()(const T&) const {} +}; + +template (), std::declval()))> +constexpr bool has_visit(int) { + return true; +} + +template +constexpr bool has_visit(...) { + return false; +} + +void test_sfinae() { + struct BadVariant : std::variant, std::variant {}; + struct BadVariant2 : private std::variant {}; + struct GoodVariant : std::variant {}; + struct GoodVariant2 : GoodVariant {}; + + static_assert(!has_visit(0)); + static_assert(!has_visit(0)); + static_assert(!has_visit(0)); + static_assert(has_visit>(0)); + static_assert(has_visit(0)); + static_assert(has_visit(0)); +} + int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); @@ -355,6 +435,8 @@ int main(int, char**) { test_constexpr(); test_exceptions(); test_caller_accepts_nonconst(); + test_derived_from_variant(); + test_sfinae(); return 0; } 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 index 7e569e2e9e42bd..459b75f717f517 100644 --- 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 @@ -411,6 +411,99 @@ void test_constexpr_explicit_side_effect() { static_assert(test_lambda(202) == 202, ""); } +void test_derived_from_variant() { + struct MyVariant : std::variant {}; + + std::visit( + [](auto x) { + assert(x == 42); + return true; + }, + MyVariant{42}); + std::visit( + [](auto x) { + assert(x == -1.3f); + return true; + }, + MyVariant{-1.3f}); + + // 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, + std::tuple, + EvilVariantBase { + using std::variant::variant; + }; + + std::visit( + [](auto x) { + assert(x == 12); + return true; + }, + EvilVariant1{12}); + std::visit( + [](auto x) { + assert(x == 12.3); + return true; + }, + EvilVariant1{12.3}); + + // Check that visit unambiguously picks the variant, even if the other base has __impl member. + struct ImplVariantBase { + struct Callable { + bool operator()(); + }; + + Callable __impl; + }; + + struct EvilVariant2 : std::variant, ImplVariantBase { + using std::variant::variant; + }; + + std::visit( + [](auto x) { + assert(x == 12); + return true; + }, + EvilVariant2{12}); + std::visit( + [](auto x) { + assert(x == 12.3); + return true; + }, + EvilVariant2{12.3}); +} + +struct any_visitor { + template + bool operator()(const T&) { + return true; + } +}; + +template ( + std::declval(), std::declval()))> +constexpr bool has_visit(int) { + return true; +} + +template +constexpr bool has_visit(...) { + return false; +} + +void test_sfinae() { + struct BadVariant : std::variant, std::variant {}; + + static_assert(has_visit >(int())); + static_assert(!has_visit(int())); +} + int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); @@ -425,6 +518,8 @@ int main(int, char**) { test_exceptions(); test_caller_accepts_nonconst(); test_constexpr_explicit_side_effect(); + test_derived_from_variant(); + test_sfinae(); return 0; } diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index d6fa8361853fb4..c3db978f05f780 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -627,7 +627,7 @@ def add_version_header(tc): "headers": ["functional"], }, { "name": "__cpp_lib_variant", - "values": { "c++17": 201606 }, + "values": { "c++17": 202102 }, "headers": ["variant"], }, { "name": "__cpp_lib_void_t",