diff --git a/libcxx/docs/Cxx2bStatusPaperStatus.csv b/libcxx/docs/Cxx2bStatusPaperStatus.csv --- a/libcxx/docs/Cxx2bStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2bStatusPaperStatus.csv @@ -5,7 +5,7 @@ "`P1679R3 `__","LWG","string contains function","Autumn 2020","|Complete|","12.0" "","","","","","" "`P1682R3 `__","LWG","std::to_underlying for enumerations","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 @@ -301,5 +301,7 @@ ``__cpp_lib_stdatomic_h`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_string_contains`` ``202011L`` + ------------------------------------------------- ----------------- + ``__cpp_lib_variant`` ``202102L`` ================================================= ================= 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 @@ -565,7 +592,7 @@ static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor, _Vs&&... __vs) { return __base::__visit_alt(_VSTD::forward<_Visitor>(__visitor), - _VSTD::forward<_Vs>(__vs).__impl...); + _VSTD::__as_variant(_VSTD::forward<_Vs>(__vs)).__impl...); } template @@ -1639,7 +1666,7 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr void __throw_if_valueless(_Vs&&... __vs) { - const bool __valueless = (... || __vs.valueless_by_exception()); + const bool __valueless = (... || _VSTD::__as_variant(__vs).valueless_by_exception()); if (__valueless) { __throw_bad_variant_access(); } diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -159,7 +159,8 @@ __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 + 201606L // C++17 __cpp_lib_void_t 201411L */ @@ -365,6 +366,8 @@ // # define __cpp_lib_stacktrace 202011L // # define __cpp_lib_stdatomic_h 202011L # define __cpp_lib_string_contains 202011L +# undef __cpp_lib_variant +# define __cpp_lib_variant 202102L #endif // clang-format on 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 @@ -17,6 +17,7 @@ /* Constant Value __cpp_lib_variant 201606L [C++17] + 202102L [C++2b] */ #include @@ -57,8 +58,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,6 +150,7 @@ __cpp_lib_unordered_map_try_emplace 201411L [C++17] __cpp_lib_unwrap_ref 201811L [C++20] __cpp_lib_variant 201606L [C++17] + 202102L [C++2b] __cpp_lib_void_t 201411L [C++17] */ @@ -4527,8 +4528,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 pick 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,44 @@ 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 pick 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}); +} + int main(int, char**) { test_call_operator_forwarding(); test_argument_forwarding(); @@ -355,6 +393,7 @@ test_constexpr(); test_exceptions(); test_caller_accepts_nonconst(); + 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 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -628,7 +628,7 @@ "headers": ["functional"], }, { "name": "__cpp_lib_variant", - "values": { "c++17": 201606 }, + "values": { "c++17": 201606, "c++2b": 202102 }, "headers": ["variant"], }, { "name": "__cpp_lib_void_t",