From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id B3260385842E; Fri, 15 Oct 2021 17:27:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B3260385842E MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r12-4447] libstdc++: Add missing constexpr to std::variant (P2231R1) X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: e27771e5dcd8cf2cb757db6177a3485acd28b88f X-Git-Newrev: ad820b0bb5f8342a8db2831d1f15c103583a3ba0 Message-Id: <20211015172726.B3260385842E@sourceware.org> Date: Fri, 15 Oct 2021 17:27:26 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Oct 2021 17:27:26 -0000 https://gcc.gnu.org/g:ad820b0bb5f8342a8db2831d1f15c103583a3ba0 commit r12-4447-gad820b0bb5f8342a8db2831d1f15c103583a3ba0 Author: Jonathan Wakely Date: Thu Oct 14 13:27:03 2021 +0100 libstdc++: Add missing constexpr to std::variant (P2231R1) This implements the changes in P2231R1 which make std::variant fully constexpr in C++20. We need to replace placement new with std::construct_at, but that isn't defined for C++17. Use std::_Construct instead, which forwards to std::construct_at in C++20 mode (since the related changes to make std::optional fully constexpr, in r12-4389). We also need to replace the untyped char buffer in _Uninitialized with a union, which can be accessed in constexpr functions. But the union needs to have a non-trivial destructor if its variant type is non-trivial, which means that the _Variadic_union also needs a non-trivial destructor. This adds a constrained partial specialization of _Variadic_union for the C++20-only case where a non-trivial destructor is needed. We can't use concepts to constrain the specialization (or the primary template's destructor) in C++17, so retain the untyped char buffer solution for C++17 mode. libstdc++-v3/ChangeLog: * include/std/variant (__cpp_lib_variant): Update value for C++20. (__variant_cast, __variant_construct): Add constexpr for C++20. (__variant_construct_single, __construct_by_index) Likewise. Use std::_Construct instead of placement new. (_Uninitialized) [__cplusplus >= 202002]: Replace buffer with a union and define a destructor. (_Variadic_union) [__cplusplus >= 202002]: Add a specialization for non-trivial destruction. (_Variant_storage::__index_of): New helper variable template. (_Variant_storage::~_Variant_storage()): Add constexpr. (_Variant_storage::_M_reset()): Likewise. (_Copy_ctor_base, _Move_ctor_base): Likewise. (_Copy_assign_base, _Move_assign_base): Likewise. (variant, swap): Likewise. * include/std/version (__cpp_lib_variant): Update value for C++20. * testsuite/20_util/optional/version.cc: Check for exact value in C++17. * testsuite/20_util/variant/87619.cc: Increase timeout for C++20 mode. * testsuite/20_util/variant/constexpr.cc: New test. * testsuite/20_util/variant/version.cc: New test. Diff: --- libstdc++-v3/include/std/variant | 137 ++++++++++++++++---- libstdc++-v3/include/std/version | 7 +- libstdc++-v3/testsuite/20_util/optional/version.cc | 4 +- libstdc++-v3/testsuite/20_util/variant/87619.cc | 2 + .../testsuite/20_util/variant/constexpr.cc | 138 +++++++++++++++++++++ libstdc++-v3/testsuite/20_util/variant/version.cc | 11 ++ 6 files changed, 274 insertions(+), 25 deletions(-) diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index f49094130ee..d18365fde22 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -39,13 +39,14 @@ #include #include #include -#include #include #include #include #include #include // in_place_index_t -#if __cplusplus > 201703L +#if __cplusplus == 201703L +# include +#else # include #endif @@ -71,7 +72,12 @@ namespace __variant } // namespace __variant } // namespace __detail -#define __cpp_lib_variant 202102L +#if __cplusplus >= 202002L && __cpp_concepts +// P2231R1 constexpr needs constexpr unions and constrained destructors. +# define __cpp_lib_variant 202106L +#else +# define __cpp_lib_variant 202102L +#endif template class tuple; template class variant; @@ -146,6 +152,7 @@ namespace __variant __do_visit(_Visitor&& __visitor, _Variants&&... __variants); template + _GLIBCXX20_CONSTEXPR decltype(auto) __variant_cast(_Tp&& __rhs) { @@ -224,8 +231,12 @@ namespace __variant __as(const std::variant<_Types...>&& __v) noexcept { return std::move(__v); } + // For C++17: // _Uninitialized is guaranteed to be a trivially destructible type, // even if T is not. + // For C++20: + // _Uninitialized is trivially destructible iff T is, so _Variant_union + // needs a constrained non-trivial destructor. template> struct _Uninitialized; @@ -256,6 +267,37 @@ namespace __variant template struct _Uninitialized<_Type, false> { +#if __cpp_lib_variant >= 202106L + template + constexpr + _Uninitialized(in_place_index_t<0>, _Args&&... __args) + : _M_storage(std::forward<_Args>(__args)...) + { } + + constexpr ~_Uninitialized() { } + + _Uninitialized(const _Uninitialized&) = default; + _Uninitialized(_Uninitialized&&) = default; + _Uninitialized& operator=(const _Uninitialized&) = default; + _Uninitialized& operator=(_Uninitialized&&) = default; + + constexpr const _Type& _M_get() const & noexcept + { return _M_storage; } + + constexpr _Type& _M_get() & noexcept + { return _M_storage; } + + constexpr const _Type&& _M_get() const && noexcept + { return std::move(_M_storage); } + + constexpr _Type&& _M_get() && noexcept + { return std::move(_M_storage); } + + union { + char _M_nope; + _Type _M_storage; + }; +#else template constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) @@ -277,6 +319,7 @@ namespace __variant { return std::move(*_M_storage._M_ptr()); } __gnu_cxx::__aligned_membuf<_Type> _M_storage; +#endif }; template @@ -353,15 +396,31 @@ namespace __variant constexpr _Variadic_union() : _M_rest() { } template - constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args) + constexpr + _Variadic_union(in_place_index_t<0>, _Args&&... __args) : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) { } template - constexpr _Variadic_union(in_place_index_t<_Np>, _Args&&... __args) + constexpr + _Variadic_union(in_place_index_t<_Np>, _Args&&... __args) : _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...) { } +#if __cpp_lib_variant >= 202106L + _Variadic_union(const _Variadic_union&) = default; + _Variadic_union(_Variadic_union&&) = default; + _Variadic_union& operator=(const _Variadic_union&) = default; + _Variadic_union& operator=(_Variadic_union&&) = default; + + ~_Variadic_union() = default; + + constexpr ~_Variadic_union() + requires (!__has_trivial_destructor(_First)) + || (!__has_trivial_destructor(_Variadic_union<_Rest...>)) + { } +#endif + _Uninitialized<_First> _M_first; _Variadic_union<_Rest...> _M_rest; }; @@ -406,6 +465,10 @@ namespace __variant template struct _Variant_storage { + template + static constexpr size_t __index_of + = __detail::__variant::__index_of_v<_Tp, _Types...>; + constexpr _Variant_storage() : _M_index(static_cast<__index_type>(variant_npos)) @@ -418,7 +481,8 @@ namespace __variant _M_index{_Np} { } - void _M_reset() + constexpr void + _M_reset() { if (!_M_valid()) [[unlikely]] return; @@ -431,6 +495,7 @@ namespace __variant _M_index = static_cast<__index_type>(variant_npos); } + _GLIBCXX20_CONSTEXPR ~_Variant_storage() { _M_reset(); } @@ -450,6 +515,10 @@ namespace __variant template struct _Variant_storage { + template + static constexpr size_t __index_of + = __detail::__variant::__index_of_v<_Tp, _Types...>; + constexpr _Variant_storage() : _M_index(static_cast<__index_type>(variant_npos)) @@ -462,7 +531,8 @@ namespace __variant _M_index{_Np} { } - void _M_reset() noexcept + constexpr void + _M_reset() noexcept { _M_index = static_cast<__index_type>(variant_npos); } constexpr bool @@ -489,17 +559,25 @@ namespace __variant _Variant_storage<_Traits<_Types...>::_S_trivial_dtor, _Types...>; template - void __variant_construct_single(_Tp&& __lhs, _Up&& __rhs_mem) + _GLIBCXX20_CONSTEXPR + void + __variant_construct_single(_Tp&& __lhs, _Up&& __rhs_mem) { - void* __storage = std::addressof(__lhs._M_u); - using _Type = remove_reference_t; + using _Type = __remove_cvref_t<_Up>; + if constexpr (!is_same_v<_Type, __variant_cookie>) - ::new (__storage) - _Type(std::forward(__rhs_mem)); + { + using _Lhs = remove_reference_t<_Tp>; + std::_Construct(std::__addressof(__lhs._M_u), + in_place_index<_Lhs::template __index_of<_Type>>, + std::forward<_Up>(__rhs_mem)); + } } template - void __variant_construct(_Tp&& __lhs, _Up&& __rhs) + _GLIBCXX20_CONSTEXPR + void + __variant_construct(_Tp&& __lhs, _Up&& __rhs) { __lhs._M_index = __rhs._M_index; __variant::__raw_visit([&__lhs](auto&& __rhs_mem) mutable @@ -518,6 +596,7 @@ namespace __variant using _Base = _Variant_storage_alias<_Types...>; using _Base::_Base; + _GLIBCXX20_CONSTEXPR _Copy_ctor_base(const _Copy_ctor_base& __rhs) noexcept(_Traits<_Types...>::_S_nothrow_copy_ctor) { @@ -546,6 +625,7 @@ namespace __variant using _Base = _Copy_ctor_alias<_Types...>; using _Base::_Base; + _GLIBCXX20_CONSTEXPR _Move_ctor_base(_Move_ctor_base&& __rhs) noexcept(_Traits<_Types...>::_S_nothrow_move_ctor) { @@ -553,6 +633,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR void _M_destructive_move(unsigned short __rhs_index, _Up&& __rhs) { this->_M_reset(); @@ -561,6 +642,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR void _M_destructive_copy(unsigned short __rhs_index, const _Up& __rhs) { this->_M_reset(); @@ -580,6 +662,7 @@ namespace __variant using _Base::_Base; template + _GLIBCXX20_CONSTEXPR void _M_destructive_move(unsigned short __rhs_index, _Up&& __rhs) { this->_M_reset(); @@ -588,6 +671,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR void _M_destructive_copy(unsigned short __rhs_index, const _Up& __rhs) { this->_M_reset(); @@ -606,6 +690,7 @@ namespace __variant using _Base = _Move_ctor_alias<_Types...>; using _Base::_Base; + _GLIBCXX20_CONSTEXPR _Copy_assign_base& operator=(const _Copy_assign_base& __rhs) noexcept(_Traits<_Types...>::_S_nothrow_copy_assign) @@ -664,6 +749,7 @@ namespace __variant using _Base = _Copy_assign_alias<_Types...>; using _Base::_Base; + _GLIBCXX20_CONSTEXPR _Move_assign_base& operator=(_Move_assign_base&& __rhs) noexcept(_Traits<_Types...>::_S_nothrow_move_assign) @@ -707,8 +793,7 @@ namespace __variant using _Base = _Move_assign_alias<_Types...>; constexpr - _Variant_base() - noexcept(_Traits<_Types...>::_S_nothrow_default_ctor) + _Variant_base() noexcept(_Traits<_Types...>::_S_nothrow_default_ctor) : _Variant_base(in_place_index<0>) { } template @@ -1095,13 +1180,12 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR inline void __construct_by_index(_Variant& __v, _Args&&... __args) { - auto&& __storage = __detail::__variant::__get<_Np>(__v); - ::new ((void*)std::addressof(__storage)) - remove_reference_t - (std::forward<_Args>(__args)...); + std::_Construct(std::__addressof(__variant::__get<_Np>(__v)), + std::forward<_Args>(__args)...); // Construction didn't throw, so can set the new index now: __v._M_index = _Np; } @@ -1285,6 +1369,7 @@ namespace __variant visit(_Visitor&&, _Variants&&...); template + _GLIBCXX20_CONSTEXPR inline enable_if_t<(is_move_constructible_v<_Types> && ...) && (is_swappable_v<_Types> && ...)> swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) @@ -1342,9 +1427,11 @@ namespace __variant { private: template - friend decltype(auto) __variant_cast(_Tp&&); + friend _GLIBCXX20_CONSTEXPR decltype(auto) + __variant_cast(_Tp&&); + template - friend void + friend _GLIBCXX20_CONSTEXPR void __detail::__variant::__construct_by_index(_Variant& __v, _Args&&... __args); @@ -1402,7 +1489,7 @@ namespace __variant variant(variant&&) = default; variant& operator=(const variant&) = default; variant& operator=(variant&&) = default; - ~variant() = default; + _GLIBCXX20_CONSTEXPR ~variant() = default; template, @@ -1459,6 +1546,7 @@ namespace __variant { } template + _GLIBCXX20_CONSTEXPR enable_if_t<__exactly_once<__accepted_type<_Tp&&>> && is_constructible_v<__accepted_type<_Tp&&>, _Tp> && is_assignable_v<__accepted_type<_Tp&&>&, _Tp>, @@ -1483,6 +1571,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR enable_if_t && __exactly_once<_Tp>, _Tp&> emplace(_Args&&... __args) @@ -1492,6 +1581,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR enable_if_t&, _Args...> && __exactly_once<_Tp>, _Tp&> @@ -1502,6 +1592,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR enable_if_t, _Args...>, variant_alternative_t<_Np, variant>&> @@ -1548,6 +1639,7 @@ namespace __variant } template + _GLIBCXX20_CONSTEXPR enable_if_t, initializer_list<_Up>&, _Args...>, variant_alternative_t<_Np, variant>&> @@ -1601,6 +1693,7 @@ namespace __variant return size_t(__index_type(this->_M_index + 1)) - 1; } + _GLIBCXX20_CONSTEXPR void swap(variant& __rhs) noexcept((__is_nothrow_swappable<_Types>::value && ...) diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index a395c05db2d..0d7ae3bf857 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -173,7 +173,9 @@ # define __cpp_lib_to_chars 201611L #endif #define __cpp_lib_unordered_map_try_emplace 201411 -#define __cpp_lib_variant 202102L +#if __cplusplus == 201703L || ! __cpp_concepts // N.B. updated value in C++20 +# define __cpp_lib_variant 202102L +#endif #endif #if __cplusplus >= 202002L @@ -280,6 +282,9 @@ # endif #define __cpp_lib_to_address 201711L #define __cpp_lib_to_array 201907L +#if __cpp_concepts +# define __cpp_lib_variant 202106L +#endif #endif #if __cplusplus > 202002L diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc b/libstdc++-v3/testsuite/20_util/optional/version.cc index d8c9851f28f..c18ecb8d48d 100644 --- a/libstdc++-v3/testsuite/20_util/optional/version.cc +++ b/libstdc++-v3/testsuite/20_util/optional/version.cc @@ -4,8 +4,8 @@ #ifndef __cpp_lib_optional # error "Feature test macro for optional is missing in " -#elif __cpp_lib_optional < 201606L -# error "Feature test macro for optional has wrong value in " +#elif __cplusplus == 201703L && __cpp_lib_optional != 201606L +# error "Feature test macro for optional has wrong value for C++17 in " #elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L # error "Feature test macro for optional has wrong value for C++20 in " #endif diff --git a/libstdc++-v3/testsuite/20_util/variant/87619.cc b/libstdc++-v3/testsuite/20_util/variant/87619.cc index e83fa0306b6..c87851007f2 100644 --- a/libstdc++-v3/testsuite/20_util/variant/87619.cc +++ b/libstdc++-v3/testsuite/20_util/variant/87619.cc @@ -16,6 +16,8 @@ // . // { dg-do compile { target c++17 } } +// FIXME: Need increased timeout due to PR c++/102780 +// { dg-timeout-factor 2 { target c++20 } } #include #include diff --git a/libstdc++-v3/testsuite/20_util/variant/constexpr.cc b/libstdc++-v3/testsuite/20_util/variant/constexpr.cc new file mode 100644 index 00000000000..7af3d98fe5d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/constexpr.cc @@ -0,0 +1,138 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include + +// P2231R1 Missing constexpr in std::optional and std::variant + +#ifndef __cpp_lib_variant +#error "Feature test macro for variant is missing in " +#elif __cpp_lib_variant < 202106L +# error "Feature test macro for variant has wrong value for C++20 in " +#endif + +#include + + +constexpr bool +test_assign() +{ + std::variant v1(1); + v1 = 2.5; + VERIFY( std::get(v1) == 2.5 ); + + v1 = 99; + VERIFY( std::get(v1) == 99 ); + v1 = 999; + VERIFY( std::get(v1) == 999 ); + + struct S // non-trivial special members + { + constexpr S(int i) : i(i) { } + constexpr ~S() { } + constexpr S(const S& s) : i(s.i) { } + + int i; + }; + + std::variant v; + v = S(123); + VERIFY( std::get<1>(v).i == 123 ); + + const S s(456); + v = s; + VERIFY( std::get<1>(v).i == 456 ); + + v = 789; + VERIFY( std::get<0>(v) == 789 ); + + return true; +} + +static_assert( test_assign() ); + +constexpr bool +test_emplace() +{ + struct S // non-trivial special members + { + constexpr S(std::initializer_list l) : i(l.begin()[0]) { } + constexpr S(std::initializer_list l, int n) : i(l.begin()[n]) { } + constexpr ~S() { } + constexpr S(const S& s) : i(s.i) { } + + int i; + }; + + std::variant v(1); + + // template constexpr T& emplace(Args&&... args); + v.emplace(2.0); + VERIFY( std::get<1>(v) == 2.0 ); + v.emplace(2.5); + VERIFY( std::get<1>(v) == 2.5 ); + v.emplace(2.5); + VERIFY( std::get<0>(v) == 2 ); + + // template + // constexpr T& emplace(initializer_list, Args&&... args); + v.emplace({3, 2, 1}); + VERIFY( std::get<2>(v).i == 3 ); + v.emplace({3, 2, 1}, 1); + VERIFY( std::get<2>(v).i == 2 ); + + // template + // constexpr variant_alternative_t& emplace(Args&&... args); + v.emplace<1>(3.0); + VERIFY( std::get<1>(v) == 3.0 ); + v.emplace<1>(0.5); + VERIFY( std::get<1>(v) == 0.5 ); + v.emplace<0>(1.5); + VERIFY( std::get<0>(v) == 1 ); + + // template + // constexpr variant_alternative_t& + // emplace(initializer_list, Args&&... args); + v.emplace<2>({7, 8, 9}); + VERIFY( std::get<2>(v).i == 7 ); + v.emplace<2>({13, 12, 11}, 1); + VERIFY( std::get<2>(v).i == 12 ); + + return true; +} + +static_assert( test_emplace() ); + +constexpr bool +test_swap() +{ + std::variant v1(1), v2(2.5); + v1.swap(v2); + VERIFY( std::get(v1) == 2.5 ); + VERIFY( std::get(v2) == 1 ); + + swap(v1, v2); + VERIFY( std::get(v1) == 1 ); + VERIFY( std::get(v2) == 2.5 ); + + struct S + { + constexpr S(int i) : i(i) { } + constexpr S(S&& s) : i(s.i) { } + constexpr S& operator=(S&& s) { i = s.i; s.i = -1; return *this; } + + int i; + }; + + std::variant v3(3), v4(S(4)); + v3.swap(v4); + VERIFY( std::get(v3).i == 4 ); + VERIFY( std::get(v4) == 3 ); + v3.swap(v4); + VERIFY( std::get(v3) == 3 ); + VERIFY( std::get(v4).i == 4 ); + + return true; +} + +static_assert( test_swap() ); diff --git a/libstdc++-v3/testsuite/20_util/variant/version.cc b/libstdc++-v3/testsuite/20_util/variant/version.cc new file mode 100644 index 00000000000..de04c5eb294 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/version.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++17 } } + +#include + +#ifndef __cpp_lib_variant +#error "Feature test macro for variant is missing in " +#elif __cplusplus == 201703L && __cpp_lib_variant != 202102L +# error "Feature test macro for variant has wrong value for C++17 in " +#elif __cplusplus >= 202002L && __cpp_lib_variant < 202106L +# error "Feature test macro for variant has wrong value for C++20 in " +#endif