@@ -1122,6 +1122,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_impl_reflection=202603L");
else
cpp_warn (pfile, "__cpp_impl_reflection");
+ cpp_define (pfile, "__cpp_trivial_union=202603L");
}
if (flag_concepts && cxx_dialect > cxx14)
cpp_define (pfile, "__cpp_concepts=202002L");
@@ -2771,6 +2771,7 @@ walk_field_subobs (tree fields, special_
bad = false;
if (CP_TYPE_CONST_P (mem_type)
+ && TREE_CODE (ctx) != UNION_TYPE
&& default_init_uninitialized_part (mem_type))
{
if (diag)
@@ -2847,6 +2848,15 @@ walk_field_subobs (tree fields, special_
else
argtype = NULL_TREE;
+ if (cxx_dialect >= cxx26 && TREE_CODE (ctx) == UNION_TYPE)
+ {
+ if (sfk == sfk_constructor || sfk == sfk_inheriting_constructor)
+ continue;
+
+ if (sfk == sfk_destructor && DECL_INITIAL (field) == NULL_TREE)
+ continue;
+ }
+
rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
@@ -2985,6 +2995,38 @@ synthesized_method_walk (tree ctype, spe
/* The synthesized method will call base dtors, but check complete
here to avoid having to deal with VTT. */
fnname = complete_dtor_identifier;
+
+ if (TREE_CODE (ctype) == UNION_TYPE
+ && cxx_dialect >= cxx26
+ && deleted_p
+ /* For still incomplete types defer this until ctor cloning. */
+ && COMPLETE_TYPE_P (ctype))
+ {
+ /* [class.dtor]/(7.2.1):
+ A defaulted destructor for a class X is defined as deleted if X
+ is a union and
+ -- overload resolution to select a constructor to
+ default-initialize an object of type X either fails or selects
+ a constructor that is either deleted or user-provided. */
+ tree ctor = locate_ctor (ctype);
+ if (ctor == NULL_TREE)
+ {
+ *deleted_p = true;
+ if (diag)
+ inform (DECL_SOURCE_LOCATION (TYPE_NAME (ctype)),
+ "default constructor of %qT unusable", ctype);
+ return;
+ }
+ else if (user_provided_p (ctor))
+ {
+ *deleted_p = true;
+ if (diag)
+ inform (DECL_SOURCE_LOCATION (TYPE_NAME (ctype)),
+ "default constructor of %qT is user-provided",
+ ctype);
+ return;
+ }
+ }
}
else if (SFK_ASSIGN_P (sfk))
fnname = assign_op_identifier;
@@ -3903,17 +3903,25 @@ check_field_decl (tree field,
else
{
TYPE_NEEDS_CONSTRUCTING (t) |= TYPE_NEEDS_CONSTRUCTING (type);
- TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
- |= TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type);
TYPE_HAS_COMPLEX_COPY_ASSIGN (t)
|= (TYPE_HAS_COMPLEX_COPY_ASSIGN (type)
|| !TYPE_HAS_COPY_ASSIGN (type));
TYPE_HAS_COMPLEX_COPY_CTOR (t) |= (TYPE_HAS_COMPLEX_COPY_CTOR (type)
|| !TYPE_HAS_COPY_CTOR (type));
- TYPE_HAS_COMPLEX_MOVE_ASSIGN (t) |= TYPE_HAS_COMPLEX_MOVE_ASSIGN (type);
+ TYPE_HAS_COMPLEX_MOVE_ASSIGN (t)
+ |= TYPE_HAS_COMPLEX_MOVE_ASSIGN (type);
TYPE_HAS_COMPLEX_MOVE_CTOR (t) |= TYPE_HAS_COMPLEX_MOVE_CTOR (type);
- TYPE_HAS_COMPLEX_DFLT (t) |= (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type)
- || TYPE_HAS_COMPLEX_DFLT (type));
+ /* In C++26, triviality of default ctor or dtor of a variant member
+ doesn't matter for triviality of the t's default ctor or dtor. */
+ if (cxx_dialect < cxx26
+ || TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
+ {
+ TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+ |= TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type);
+ TYPE_HAS_COMPLEX_DFLT (t)
+ |= (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type)
+ || TYPE_HAS_COMPLEX_DFLT (type));
+ }
}
if (TYPE_HAS_COPY_CTOR (type)
@@ -5528,7 +5536,26 @@ clone_constructors_and_destructors (tree
clone_cdtor (fn, /*update_methods=*/true);
if (tree dtor = CLASSTYPE_DESTRUCTOR (t))
- clone_cdtor (dtor, /*update_methods=*/true);
+ {
+ if (cxx_dialect >= cxx26
+ && TREE_CODE (t) == UNION_TYPE
+ && DECL_DEFAULTED_IN_CLASS_P (dtor)
+ && !DECL_DELETED_FN (dtor))
+ {
+ /* [class.dtor]/(7.2.1):
+ A defaulted destructor for a class X is defined as deleted if X
+ is a union and
+ -- overload resolution to select a constructor to
+ default-initialize an object of type X either fails or selects
+ a constructor that is either deleted or user-provided.
+ This isn't done by synthesized_method_walk for incomplete types
+ because the default ctor is not yet cloned. */
+ tree ctor = locate_ctor (t);
+ if (ctor == NULL_TREE || user_provided_p (ctor))
+ DECL_DELETED_FN (dtor) = true;
+ }
+ clone_cdtor (dtor, /*update_methods=*/true);
+ }
}
/* Deduce noexcept for a destructor DTOR. */
@@ -94,7 +94,7 @@
#undef __cpp_template_parameters
#undef __cpp_template_template_args // { dg-warning "undefining '__cpp_template_template_args'" "" { target c++20 } }
#undef __cpp_threadsafe_static_init // { dg-warning "undefining '__cpp_threadsafe_static_init'" "" { target c++20 } }
-#undef __cpp_trivial_union
+#undef __cpp_trivial_union // { dg-warning "undefining '__cpp_trivial_union'" "" { target c++26 } }
#undef __cpp_unicode_characters // { dg-warning "undefining '__cpp_unicode_characters'" "" { target c++20 } }
#undef __cpp_unicode_literals // { dg-warning "undefining '__cpp_unicode_literals'" "" { target c++20 } }
#undef __cpp_user_defined_literals // { dg-warning "undefining '__cpp_user_defined_literals'" "" { target c++20 } }
@@ -95,7 +95,7 @@
#define __cpp_template_parameters 202502L
#define __cpp_template_template_args 201611L // { dg-error "'__cpp_template_template_args' redefined" "" { target c++20 } }
#define __cpp_threadsafe_static_init 200806L // { dg-error "'__cpp_threadsafe_static_init' redefined" "" { target c++20 } }
-#define __cpp_trivial_union 202502L
+#define __cpp_trivial_union 202603L // { dg-error "'__cpp_trivial_union' redefined" "" { target c++26 } }
#define __cpp_unicode_characters 200704L // { dg-error "'__cpp_unicode_characters' redefined" "" { target c++17 } }
#define __cpp_unicode_literals 200710L // { dg-error "'__cpp_unicode_literals' redefined" "" { target c++20 } }
#define __cpp_user_defined_literals 200809L // { dg-error "'__cpp_user_defined_literals' redefined" "" { target c++20 } }
@@ -652,3 +652,9 @@
#elif __cpp_expansion_statements != 202506
# error "__cpp_expansion_statements != 202506"
#endif
+
+#ifndef __cpp_trivial_union
+# error "__cpp_trivial_union"
+#elif __cpp_trivial_union != 202603
+# error "__cpp_trivial_union != 202603"
+#endif
@@ -0,0 +1,73 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++11 } }
+
+#include <type_traits>
+
+// These two were incorrectly deleted.
+union A { int a; const int b; };
+static_assert (std::is_default_constructible <A>::value, "");
+static_assert (std::is_trivially_default_constructible <A>::value, "");
+static_assert (std::is_destructible <A>::value, "");
+static_assert (std::is_trivially_destructible <A>::value, "");
+struct B { int a; union { int b; const int c; }; };
+static_assert (std::is_default_constructible <B>::value, "");
+static_assert (std::is_trivially_default_constructible <B>::value, "");
+static_assert (std::is_destructible <B>::value, "");
+static_assert (std::is_trivially_destructible <B>::value, "");
+// C::C() is incorrectly not deleted in C++11 to 23, but in C++26 it should
+// not be deleted.
+union C { const int a = 42; const long b; ~C (); };
+#if __cpp_trivial_union >= 202502L
+static_assert (std::is_default_constructible <C>::value, "");
+static_assert (!std::is_trivially_default_constructible <C>::value, "");
+static_assert (std::is_destructible <C>::value, "");
+static_assert (!std::is_trivially_destructible <C>::value, "");
+#endif
+struct D { D () = delete; D (int); ~D () = default; };
+union E { D a = 42; D b; ~E (); };
+static_assert (std::is_default_constructible <E>::value, "");
+static_assert (std::is_destructible <E>::value, "");
+struct F { int a; union { D b = 42; D c; }; };
+static_assert (std::is_default_constructible <F>::value, "");
+static_assert (std::is_destructible <F>::value, "");
+struct G { G (); ~G (); };
+union I { int a; const int b; ~I (); };
+static_assert (std::is_default_constructible <I>::value, "");
+static_assert (!std::is_trivially_default_constructible <I>::value, "");
+static_assert (std::is_destructible <I>::value, "");
+static_assert (!std::is_trivially_destructible <I>::value, "");
+union J { D a; int b; };
+#if __cpp_trivial_union >= 202502L
+static_assert (std::is_default_constructible <J>::value, "");
+static_assert (std::is_trivially_default_constructible <J>::value, "");
+#else
+static_assert (!std::is_default_constructible <J>::value, "");
+static_assert (!std::is_trivially_default_constructible <J>::value, "");
+#endif
+static_assert (std::is_destructible <J>::value, "");
+static_assert (std::is_trivially_destructible <J>::value, "");
+union K { G a; int b; };
+#if __cpp_trivial_union >= 202502L
+static_assert (std::is_default_constructible <K>::value, "");
+static_assert (std::is_trivially_default_constructible <K>::value, "");
+static_assert (std::is_destructible <K>::value, "");
+static_assert (std::is_trivially_destructible <K>::value, "");
+#else
+static_assert (!std::is_default_constructible <K>::value, "");
+static_assert (!std::is_trivially_default_constructible <K>::value, "");
+static_assert (!std::is_destructible <K>::value, "");
+static_assert (!std::is_trivially_destructible <K>::value, "");
+#endif
+struct L { int a; union { G b; int c; }; };
+#if __cpp_trivial_union >= 202502L
+static_assert (std::is_default_constructible <L>::value, "");
+static_assert (std::is_trivially_default_constructible <L>::value, "");
+static_assert (std::is_destructible <L>::value, "");
+static_assert (std::is_trivially_destructible <L>::value, "");
+#else
+static_assert (!std::is_default_constructible <L>::value, "");
+static_assert (!std::is_trivially_default_constructible <L>::value, "");
+static_assert (!std::is_destructible <L>::value, "");
+static_assert (!std::is_trivially_destructible <L>::value, "");
+#endif
@@ -0,0 +1,108 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+#include <ranges>
+
+using namespace std::meta;
+constexpr auto ctx = std::meta::access_context::unchecked ();
+union A { int a; const int b; };
+static_assert (is_default_constructible_type (^^A));
+static_assert (is_trivially_default_constructible_type (^^A));
+static_assert (is_destructible_type (^^A));
+static_assert (is_trivially_destructible_type (^^A));
+constexpr auto Actor = (members_of (^^A, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Adtor = (members_of (^^A, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Actor) && !is_deleted (Actor));
+static_assert (is_defaulted (Adtor) && !is_deleted (Adtor));
+struct B { int a; union { int b; const int c; }; };
+static_assert (is_default_constructible_type (^^B));
+static_assert (is_trivially_default_constructible_type (^^B));
+static_assert (is_destructible_type (^^B));
+static_assert (is_trivially_destructible_type (^^B));
+constexpr auto Bctor = (members_of (^^B, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Bdtor = (members_of (^^B, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Bctor) && !is_deleted (Bctor));
+static_assert (is_defaulted (Bdtor) && !is_deleted (Bdtor));
+union C { const int a = 42; const long b; ~C (); };
+static_assert (is_default_constructible_type (^^C));
+static_assert (!is_trivially_default_constructible_type (^^C));
+static_assert (is_destructible_type (^^C));
+static_assert (!is_trivially_destructible_type (^^C));
+constexpr auto Cctor = (members_of (^^C, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Cdtor = (members_of (^^C, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Cctor) && !is_deleted (Cctor));
+static_assert (!is_defaulted (Cdtor) && !is_deleted (Cdtor));
+struct D { D () = delete; D (int); ~D () = default; };
+union E { D a = 42; D b; ~E (); };
+static_assert (is_default_constructible_type (^^E));
+static_assert (is_destructible_type (^^E));
+constexpr auto Ector = (members_of (^^E, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Edtor = (members_of (^^E, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Ector) && !is_deleted (Ector));
+static_assert (!is_defaulted (Edtor) && !is_deleted (Edtor));
+struct F { int a; union { D b = 42; D c; }; };
+static_assert (is_default_constructible_type (^^F));
+static_assert (is_destructible_type (^^F));
+constexpr auto Fctor = (members_of (^^F, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Fdtor = (members_of (^^F, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Fctor) && !is_deleted (Fctor));
+static_assert (is_defaulted (Fdtor) && !is_deleted (Fdtor));
+struct G { G (); ~G (); };
+union I { int a; const int b; ~I (); };
+static_assert (is_default_constructible_type (^^I));
+static_assert (!is_trivially_default_constructible_type (^^I));
+static_assert (is_destructible_type (^^I));
+static_assert (!is_trivially_destructible_type (^^I));
+constexpr auto Ictor = (members_of (^^I, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Idtor = (members_of (^^I, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Ictor) && !is_deleted (Ictor));
+static_assert (!is_defaulted (Idtor) && !is_deleted (Idtor));
+union J { D a; int b; };
+static_assert (is_default_constructible_type (^^J));
+static_assert (is_trivially_default_constructible_type (^^J));
+static_assert (is_destructible_type (^^J));
+static_assert (is_trivially_destructible_type (^^J));
+constexpr auto Jctor = (members_of (^^J, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Jdtor = (members_of (^^J, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Jctor) && !is_deleted (Jctor));
+static_assert (is_defaulted (Jdtor) && !is_deleted (Jdtor));
+union K { G a; int b; };
+static_assert (is_default_constructible_type (^^K));
+static_assert (is_trivially_default_constructible_type (^^K));
+static_assert (is_destructible_type (^^K));
+static_assert (is_trivially_destructible_type (^^K));
+constexpr auto Kctor = (members_of (^^K, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Kdtor = (members_of (^^K, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Kctor) && !is_deleted (Kctor));
+static_assert (is_defaulted (Kdtor) && !is_deleted (Kdtor));
+struct L { int a; union { G b; int c; }; };
+static_assert (is_default_constructible_type (^^L));
+static_assert (is_trivially_default_constructible_type (^^L));
+static_assert (is_destructible_type (^^L));
+static_assert (is_trivially_destructible_type (^^L));
+constexpr auto Lctor = (members_of (^^L, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Ldtor = (members_of (^^L, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (is_defaulted (Lctor) && !is_deleted (Lctor));
+static_assert (is_defaulted (Ldtor) && !is_deleted (Ldtor));
+union M { M (); int a; long b; };
+static_assert (!is_default_constructible_type (^^M));
+static_assert (!is_trivially_default_constructible_type (^^M));
+static_assert (!is_destructible_type (^^M));
+static_assert (!is_trivially_destructible_type (^^M));
+constexpr auto Mctor = (members_of (^^M, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Mdtor = (members_of (^^M, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (!is_defaulted (Mctor) && !is_deleted (Mctor));
+static_assert (is_defaulted (Mdtor) && is_deleted (Mdtor));
+struct N { N () = default; N (const N &) = default; int a; ~N (); };
+union O { O (); int a; N b; };
+static_assert (!is_default_constructible_type (^^O));
+static_assert (!is_trivially_default_constructible_type (^^O));
+static_assert (!is_destructible_type (^^O));
+static_assert (!is_trivially_destructible_type (^^O));
+constexpr auto Octor = (members_of (^^O, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
+constexpr auto Odtor = (members_of (^^O, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
+static_assert (!is_defaulted (Octor) && !is_deleted (Octor));
+static_assert (is_defaulted (Odtor) && is_deleted (Odtor));
@@ -985,7 +985,7 @@ static_assert (!is_destructible_type (^^
static_assert (!is_destructible_type (^^const N2::Del [1]));
static_assert (!is_destructible_type (^^N2::Del []));
static_assert (!is_destructible_type (^^const N2::Del []));
-static_assert (!is_destructible_type (^^N2::NontrivialUnion));
+static_assert (is_destructible_type (^^N2::NontrivialUnion));
static_assert (is_destructible_type (^^N2::UnusualCopy));
static_assert (is_trivially_default_constructible_type (^^int));
@@ -1367,8 +1367,8 @@ static_assert (!is_nothrow_destructible_
static_assert (!is_nothrow_destructible_type (^^N2::Aggr2));
static_assert (!is_nothrow_destructible_type (^^N2::Aggr2 [1]));
static_assert (!is_nothrow_destructible_type (^^N2::TD1 [1][2]));
-static_assert (!is_nothrow_destructible_type (^^N2::Ut));
-static_assert (!is_nothrow_destructible_type (^^N2::Ut [3]));
+static_assert (is_nothrow_destructible_type (^^N2::Ut));
+static_assert (is_nothrow_destructible_type (^^N2::Ut [3]));
static_assert (!is_nothrow_destructible_type (^^N2::AbstractDelDtor));
static_assert (!is_nothrow_destructible_type (^^N2::Abstract2));
static_assert (!is_nothrow_destructible_type (^^N2::Abstract3));
@@ -603,7 +603,7 @@ static_assert (!is_constructible_type (^
static_assert (!is_constructible_type (^^const DelnAny, { ^^int, ^^void * }));
static_assert (!is_constructible_type (^^DelnAny, { ^^Empty, ^^B, ^^D }));
static_assert (!is_constructible_type (^^const DelnAny, { ^^Empty, ^^B, ^^D }));
-static_assert (!is_constructible_type (^^NontrivialUnion, {}));
+static_assert (is_constructible_type (^^NontrivialUnion, {}));
static_assert (!is_constructible_type (^^NontrivialUnion, { ^^const NontrivialUnion & }));
static_assert (!is_constructible_type (^^UnusualCopy, {}));
static_assert (!is_constructible_type (^^UnusualCopy, { ^^UnusualCopy }));
@@ -221,7 +221,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Up, bool = is_trivially_destructible_v<_Up>>
union _Storage
{
+#if __cpp_trivial_union >= 202502L
+ constexpr _Storage() noexcept = default;
+#else
constexpr _Storage() noexcept : _M_empty() { }
+#endif
template<typename... _Args>
constexpr
@@ -258,7 +262,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Storage& operator=(_Storage&&) = default;
#endif
+#if __cpp_trivial_union >= 202502L
+ _Empty_byte _M_empty = {};
+#else
_Empty_byte _M_empty;
+#endif
_Up _M_value;
};