mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
libstdc++: Use union to store non-trivially destructible types in C++17 mode [PR112591]
This patch disables use of specialization _Uninitialized<_Type, false> for
non-trivially destructible types by default in C++17, and fallbacks to
the primary template, that stores the type in union directly. This makes the
ABI consistent between C++17 and C++20 (or later). This partial specialization
is no longer required after the changes introduced in r16-5961-g09bece00d0ec98.
This fixes non-conformance in C++17 mode where global variables of a variant
specialization type, were not statically-initialized for non-trivially
destructible types, even if initialization of the selected alternative could
be performed at compile time. For illustration, the following global variable
will be statically initialized after this change:
std::variant<std::unique_ptr<T>, std::unique_ptr<U>> ptr;
This constitutes an ABI break, and changes the layout of the types, that uses
the same non-trivially copyable both as the base class, as alternative of the
variant object that is first member:
struct EmptyNonTrivial { ~EmptyNonTrivial(); };
struct Affected : EmptyNonTrivial {
std::variant<EmptyNonTrivial, char> mem; // mem was at offset zero,
// will use non-zero offset now
};
After changes the layout of such types consistent with one used for empty types
with trivial destructor, or one used for any empty type in C++20 or later.
For programs affected by this change, it can be reverted in C++17 mode, by
defining _GLIBCXX_USE_VARIANT_CXX17_OLD_ABI. However, presence of this macro
has no effect in C++20 or later modes.
PR libstdc++/112591
libstdc++-v3/ChangeLog:
* include/std/variant (_Uninitialized::_M_get, __get_n)
(_Uninitialized<_Type, false>): Add _GLIBCXX_USE_VARIANT_CXX17_OLD_ABI
check to preprocessor guard.
* testsuite/20_util/variant/112591.cc: Updated tests.
* testsuite/20_util/variant/112591_compat.cc: New test.
* testsuite/20_util/variant/constinit.cc: New test.
* testsuite/20_util/variant/constinit_compat.cc: New test.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
@@ -211,7 +211,7 @@ namespace __variant
|
||||
__as(const std::variant<_Types...>&& __v) noexcept
|
||||
{ return std::move(__v); }
|
||||
|
||||
#if __cpp_lib_variant < 202106L
|
||||
#if (__cpp_lib_variant < 202106L) && defined(_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI)
|
||||
template<typename _Type, bool = std::is_trivially_destructible_v<_Type>>
|
||||
struct _Uninitialized;
|
||||
#else
|
||||
@@ -230,7 +230,7 @@ namespace __variant
|
||||
: _M_storage(std::forward<_Args>(__args)...)
|
||||
{ }
|
||||
|
||||
#if __cpp_lib_variant < 202106L
|
||||
#if (__cpp_lib_variant < 202106L) && defined(_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI)
|
||||
constexpr const _Type& _M_get() const & noexcept
|
||||
{ return _M_storage; }
|
||||
|
||||
@@ -247,10 +247,9 @@ namespace __variant
|
||||
_Type _M_storage;
|
||||
};
|
||||
|
||||
#if __cpp_lib_variant < 202106L
|
||||
#if (__cpp_lib_variant < 202106L) && defined(_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI)
|
||||
// This partial specialization is used for non-trivially destructible types
|
||||
// in C++17, so that _Uninitialized<T> is trivially destructible and can be
|
||||
// used as a union member in _Variadic_union.
|
||||
// in C++17.
|
||||
template<typename _Type>
|
||||
struct _Uninitialized<_Type, false>
|
||||
{
|
||||
@@ -291,7 +290,7 @@ namespace __variant
|
||||
return __variant::__get_n<_Np - 3>(
|
||||
std::forward<_Union>(__u)._M_rest._M_rest._M_rest);
|
||||
}
|
||||
#else
|
||||
#else // !_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI
|
||||
template<size_t _Np, typename _Union>
|
||||
constexpr auto&&
|
||||
__get_n(_Union&& __u) noexcept
|
||||
@@ -306,7 +305,7 @@ namespace __variant
|
||||
return __variant::__get_n<_Np - 3>(
|
||||
std::forward<_Union>(__u)._M_rest._M_rest._M_rest);
|
||||
}
|
||||
#endif
|
||||
#endif // !_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI
|
||||
|
||||
// Returns the typed storage for __v.
|
||||
template<size_t _Np, typename _Variant>
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
struct NonEmpty { int x; };
|
||||
struct NonTrivial
|
||||
{
|
||||
constexpr NonTrivial() : x(0) {}
|
||||
constexpr NonTrivial(int p) : x(p) {}
|
||||
~NonTrivial() {}
|
||||
|
||||
int x;
|
||||
};
|
||||
|
||||
struct TrivialEmpty {};
|
||||
struct NonTrivialEmpty { ~NonTrivialEmpty() {} };
|
||||
|
||||
@@ -23,10 +32,11 @@ bool testAlias()
|
||||
int main()
|
||||
{
|
||||
VERIFY( !testAlias<NonEmpty>() );
|
||||
VERIFY( !testAlias<NonTrivial>() );
|
||||
VERIFY( !testAlias<TrivialEmpty>() );
|
||||
#if __cplusplus >= 202002L
|
||||
#if (__cplusplus >= 202002L) || !defined(_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI)
|
||||
VERIFY( !testAlias<NonTrivialEmpty>() );
|
||||
#else
|
||||
VERIFY( testAlias<NonTrivialEmpty>() );
|
||||
#else
|
||||
VERIFY( testAlias<NonTrivialEmpty>() );
|
||||
#endif
|
||||
}
|
||||
|
||||
4
libstdc++-v3/testsuite/20_util/variant/112591_compat.cc
Normal file
4
libstdc++-v3/testsuite/20_util/variant/112591_compat.cc
Normal file
@@ -0,0 +1,4 @@
|
||||
// { dg-options "-D_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI" }
|
||||
// { dg-do run { target c++17 } }
|
||||
|
||||
#include "112591.cc"
|
||||
40
libstdc++-v3/testsuite/20_util/variant/constinit.cc
Normal file
40
libstdc++-v3/testsuite/20_util/variant/constinit.cc
Normal file
@@ -0,0 +1,40 @@
|
||||
// { dg-do compile { target c++17 } }
|
||||
|
||||
#include <variant>
|
||||
|
||||
struct NonEmpty { int x; };
|
||||
struct NonTrivial
|
||||
{
|
||||
constexpr NonTrivial() : x(0) {}
|
||||
NonTrivial(int p) : x(p) {}
|
||||
~NonTrivial() {}
|
||||
|
||||
int x;
|
||||
};
|
||||
|
||||
struct TrivialEmpty {};
|
||||
struct NonTrivialEmpty
|
||||
{
|
||||
NonTrivialEmpty() = default;
|
||||
NonTrivialEmpty(float) {}
|
||||
~NonTrivialEmpty() {}
|
||||
};
|
||||
|
||||
std::variant<NonEmpty> vNonEmpty(std::in_place_type<NonEmpty>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonEmpty>)" } }
|
||||
|
||||
std::variant<NonTrivial> vNonTrivial(std::in_place_type<NonTrivial>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonTrivial>)" } }
|
||||
|
||||
std::variant<int, NonTrivial> vNonTrivialNonConstexpr(std::in_place_index<1>, 2);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_index_t<1ul?>, int&&)" } }
|
||||
|
||||
std::variant<TrivialEmpty> vTrivialEmpty(std::in_place_type<TrivialEmpty>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<TrivialEmpty>)" } }
|
||||
|
||||
std::variant<NonTrivialEmpty> vNonTrivialEmpty(std::in_place_type<NonTrivialEmpty>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonTrivialEmpty>)" } }
|
||||
|
||||
std::variant<int, NonTrivialEmpty> vNonTrivialEmptyNonConstexpr(std::in_place_index<1>, 2.0);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_index_t<1ul?>, double&&)" } }
|
||||
|
||||
43
libstdc++-v3/testsuite/20_util/variant/constinit_compat.cc
Normal file
43
libstdc++-v3/testsuite/20_util/variant/constinit_compat.cc
Normal file
@@ -0,0 +1,43 @@
|
||||
// { dg-options "-D_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI" }
|
||||
// { dg-do compile { target c++17 } }
|
||||
|
||||
#include <variant>
|
||||
|
||||
struct NonEmpty { int x; };
|
||||
struct NonTrivial
|
||||
{
|
||||
constexpr NonTrivial() : x(0) {}
|
||||
NonTrivial(int p) : x(p) {}
|
||||
~NonTrivial() {}
|
||||
|
||||
int x;
|
||||
};
|
||||
|
||||
struct TrivialEmpty {};
|
||||
struct NonTrivialEmpty
|
||||
{
|
||||
NonTrivialEmpty() = default;
|
||||
NonTrivialEmpty(float) {}
|
||||
~NonTrivialEmpty() {}
|
||||
};
|
||||
|
||||
std::variant<NonEmpty> vNonEmpty(std::in_place_type<NonEmpty>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonEmpty>)" } }
|
||||
|
||||
std::variant<NonTrivial> vNonTrivial(std::in_place_type<NonTrivial>);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_type_t<NonTrivial>)" { target { ! c++20 } } } }
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonTrivial>)" { target c++20 } } }
|
||||
|
||||
std::variant<int, NonTrivial> vNonTrivialNonConstexpr(std::in_place_index<1>, 2);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_index_t<1ul?>, int&&)" } }
|
||||
|
||||
std::variant<TrivialEmpty> vTrivialEmpty(std::in_place_type<TrivialEmpty>);
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<TrivialEmpty>)" } }
|
||||
|
||||
std::variant<NonTrivialEmpty> vNonTrivialEmpty(std::in_place_type<NonTrivialEmpty>);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_type_t<NonTrivialEmpty>)" { target { ! c++20 } } } }
|
||||
// { dg-final { scan-assembler-dem-not "(std::in_place_type_t<NonTrivialEmpty>)" { target c++20 } } }
|
||||
|
||||
std::variant<int, NonTrivialEmpty> vNonTrivialEmptyNonConstexpr(std::in_place_index<1>, 2.0);
|
||||
// { dg-final { scan-assembler-dem "(std::in_place_index_t<1ul?>, double&&)" } }
|
||||
|
||||
Reference in New Issue
Block a user