https://gcc.gnu.org/g:b3c167b61fd75fe9820636d9f7fff7920ca9b084

commit r16-6301-gb3c167b61fd75fe9820636d9f7fff7920ca9b084
Author: Tomasz Kamiński <[email protected]>
Date:   Thu Dec 11 10:43:44 2025 +0100

    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 <[email protected]>
    Signed-off-by: Tomasz Kamiński <[email protected]>

Diff:
---
 libstdc++-v3/include/std/variant                   | 13 +++----
 libstdc++-v3/testsuite/20_util/variant/112591.cc   | 16 ++++++--
 .../testsuite/20_util/variant/112591_compat.cc     |  4 ++
 .../testsuite/20_util/variant/constinit.cc         | 40 ++++++++++++++++++++
 .../testsuite/20_util/variant/constinit_compat.cc  | 43 ++++++++++++++++++++++
 5 files changed, 106 insertions(+), 10 deletions(-)

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 51d21daf46c3..12cbfd7cc268 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -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>
diff --git a/libstdc++-v3/testsuite/20_util/variant/112591.cc 
b/libstdc++-v3/testsuite/20_util/variant/112591.cc
index b1b07c4f46e2..2c5b8c9ce78f 100644
--- a/libstdc++-v3/testsuite/20_util/variant/112591.cc
+++ b/libstdc++-v3/testsuite/20_util/variant/112591.cc
@@ -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
 }
diff --git a/libstdc++-v3/testsuite/20_util/variant/112591_compat.cc 
b/libstdc++-v3/testsuite/20_util/variant/112591_compat.cc
new file mode 100644
index 000000000000..411d1c15d54f
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/112591_compat.cc
@@ -0,0 +1,4 @@
+// { dg-options "-D_GLIBCXX_USE_VARIANT_CXX17_OLD_ABI" }
+// { dg-do run { target c++17 } }
+
+#include "112591.cc"
diff --git a/libstdc++-v3/testsuite/20_util/variant/constinit.cc 
b/libstdc++-v3/testsuite/20_util/variant/constinit.cc
new file mode 100644
index 000000000000..48d647b1006a
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/constinit.cc
@@ -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&&)" } 
}
+
diff --git a/libstdc++-v3/testsuite/20_util/variant/constinit_compat.cc 
b/libstdc++-v3/testsuite/20_util/variant/constinit_compat.cc
new file mode 100644
index 000000000000..374861728ae8
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/constinit_compat.cc
@@ -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&&)" } 
} 
+

Reply via email to