diff --git a/libcxx/docs/Cxx2bStatusPaperStatus.csv b/libcxx/docs/Cxx2bStatusPaperStatus.csv --- 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 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -162,7 +162,7 @@ ------------------------------------------------- ----------------- ``__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 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -318,6 +318,33 @@ 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 const variant<_Types...>&& +__as_variant(const variant<_Types...>&& __vs) noexcept { + return _VSTD::move(__vs); +} + +template +_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&& +__as_variant(variant<_Types...>&& __vs) noexcept { + return _VSTD::move(__vs); +} + namespace __find_detail { template @@ -564,8 +591,9 @@ 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 @@ __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 @@ 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 @@ } #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 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -160,7 +160,7 @@ __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 @@ # 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 @@ -337,7 +337,7 @@ # define __cpp_lib_latch 201907L # endif # define __cpp_lib_list_remove_return_type 201806L -# ifndef _LIBCPP_HAS_NO_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201907L # define __cpp_lib_math_constants 201907L # endif // # define __cpp_lib_polymorphic_allocator 201902L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/numbers.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/numbers.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/numbers.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numbers.version.pass.cpp @@ -42,7 +42,7 @@ #elif TEST_STD_VER == 20 -# ifndef _LIBCPP_HAS_NO_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201907L # ifndef __cpp_lib_math_constants # error "__cpp_lib_math_constants should be defined in c++20" # endif @@ -53,11 +53,11 @@ # ifdef __cpp_lib_math_constants # error "__cpp_lib_math_constants should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201907L is not defined!" # endif -# endif // _LIBCPP_HAS_NO_CONCEPTS +# endif #elif TEST_STD_VER > 20 -# ifndef _LIBCPP_HAS_NO_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201907L # ifndef __cpp_lib_math_constants # error "__cpp_lib_math_constants should be defined in c++2b" # endif @@ -68,7 +68,7 @@ # ifdef __cpp_lib_math_constants # error "__cpp_lib_math_constants should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201907L is not defined!" # endif -# endif // _LIBCPP_HAS_NO_CONCEPTS +# endif #endif // TEST_STD_VER > 20 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 --- 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 --- 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 @@ -2890,7 +2890,7 @@ # error "__cpp_lib_map_try_emplace should have the value 201411L in c++20" # endif -# ifndef _LIBCPP_HAS_NO_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201907L # ifndef __cpp_lib_math_constants # error "__cpp_lib_math_constants should be defined in c++20" # endif @@ -2901,7 +2901,7 @@ # ifdef __cpp_lib_math_constants # error "__cpp_lib_math_constants should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201907L is not defined!" # endif -# endif // _LIBCPP_HAS_NO_CONCEPTS +# endif # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_math_special_functions @@ -3301,8 +3301,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 @@ -4104,7 +4104,7 @@ # error "__cpp_lib_map_try_emplace should have the value 201411L in c++2b" # endif -# if !_LIBCPP_HAS_NO_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201907L # ifndef __cpp_lib_math_constants # error "__cpp_lib_math_constants should be defined in c++2b" # endif @@ -4115,7 +4115,7 @@ # ifdef __cpp_lib_math_constants # error "__cpp_lib_math_constants should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201907L is not defined!" # endif -# endif // !_LIBCPP_HAS_NO_CONCEPTS +# endif # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_math_special_functions @@ -4539,8 +4539,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.relops/relops.pass.cpp b/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp @@ -269,9 +269,59 @@ #endif } +template +void test_comparisons() { + assert(Variant{1} == Variant{1}); + assert(Variant{1} != Variant{12.3}); + assert(Variant{1} < Variant{12.3}); + assert(Variant{1} <= Variant{12.3}); + assert(Variant{1} > Variant{-12}); + assert(Variant{1} >= Variant{-12}); + + assert(!(Variant{1.0} == Variant{42.0})); + assert(!(Variant{1.0} != Variant{1.0})); + assert(!(Variant{1.0} < Variant{-42.0})); + assert(!(Variant{1.0} <= Variant{0.0})); + assert(!(Variant{1.0} > Variant{42.0})); + assert(!(Variant{1.0} >= Variant{42.0})); +} + +void test_derived_from_variant() { + struct MyVariant : std::variant {}; + test_comparisons(); + + // 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, EvilVariantBase { + using std::variant::variant; + }; + + test_comparisons(); + + // Check that operators 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; + }; + + test_comparisons(); +} + int main(int, char**) { test_equality(); test_relational(); + test_derived_from_variant(); return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp @@ -348,6 +348,67 @@ std::visit(Visitor{}, v); } +void test_derived_from_variant() { + struct MyVariant : std::variant {}; + + std::visit([](auto x) { assert(x == 42); }, MyVariant{42}); + std::visit([](auto x) { assert(x == -1.3f); }, 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); }, 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&) {} +}; + +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 {}; + + static_assert(has_visit >(int())); + static_assert(!has_visit(int())); +} + int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); @@ -355,6 +416,8 @@ 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 --- 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 @@ 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 @@ 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 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -631,7 +631,7 @@ "headers": ["functional"], }, { "name": "__cpp_lib_variant", - "values": { "c++17": 201606 }, + "values": { "c++17": 202102 }, "headers": ["variant"], }, { "name": "__cpp_lib_void_t",