[PATCH] c++, v3: Implement C++26 P3074R7 and CWG3189 - trivial unions [PR119059]

Jason Merrill jason@redhat.com
Thu May 21 14:03:56 GMT 2026


On 5/21/26 3:50 AM, Jakub Jelinek wrote:
> Hi!
> 
> Given the ongoing discussions in core on the start_lifetime/union elemental
> subobject discussions, the following patch extracts just the P3074R7
> minus the sentence removed again in P3726R2 and CWG3189 part of the patch,
> leaving P3726R2 plus possible CWG and/or LWG issues related to it for
> an incremental patch.
> I've extended the testsuite coverage as well (both trivial-union1.C tests).

> There is one change which doesn't affect just C++26 but also older versions
> of the standard, https://eel.is/c++draft/class.default.ctor#2.2 or its older
> counterparts, e.g. C++11 had in [class.ctor]/5
> "any non-variant non-static data member of const-qualified type (or array thereof)
> with no brace-or-equal-initializer does not have a user-provided default
> constructor"
> but we've been ignoring the "non-variant" part thereof and diagnosing it
> for variant members too.  Note, this is related to the other unimplemented
> rule I've posted a patch earlier for that was dismissed (reject
> all variant members const before C++26), so some cases which we've rejected
> for a wrong reason will now be accepted when they are still invalid before
> C++26.

So previously we accepted the case of all const but one has a DMI, which 
seems fine to me.  This would change it to accept all const with no DMI, 
which seems like a regression for pre-26.

If we have been wrongly rejecting a union with some const and some 
non-const, perhaps we should reconsider your earlier patch to address that.

> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2026-05-21  Jakub Jelinek  <jakub@redhat.com>
> 
> gcc/c-family/
> 	* c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine
> 	__cpp_trivial_union to 202502L.
> gcc/cp/
> 	* method.cc: Implement C++26 P3074R7 - trivial unions (was
> 	std::uninitialized<T>) (except the sentence removed again in P3726R2)
> 	and proposed resolution of CWG3189 - Implicitly deleted destructors
> 	for union-like classes.
> 	(walk_field_subobs): Don't do default_init_uninitialized_part checks
> 	for variant members.  Don't check subobject ctor/dtor for variant
> 	members for ctor/inheriting ctor or when subobject doesn't have member
> 	initializer for dtor and it is either the dtor_from_ctor case or
> 	the current class doesn't have user provided ctors.
> 	* class.cc (check_field_decl): Don't or in
> 	TYPE_HAS_NONTRIVIAL_DESTRUCTOR or TYPE_HAS_DEFAULT_CONSTRUCTOR of
> 	variant subobjects for C++26.
> gcc/testsuite/
> 	* g++.dg/DRs/dr2581-1.C: Expect warning for __cpp_trivial_union.
> 	* g++.dg/DRs/dr2581-2.C: Expect error for __cpp_trivial_union.
> 	* g++.dg/cpp26/feat-cxx26.C: Add __cpp_trivial_union checking.
> 	* g++.dg/cpp26/trivial-union1.C: New test.
> 	* g++.dg/cpp26/trivial-union2.C: New test.
> 	* g++.dg/reflect/trivial-union1.C: New test.
> 	* g++.dg/reflect/type_trait6.C: Adjust expected result of
> 	one is_destructible_type and two is_nothrow_destructible_type calls.
> 	* g++.dg/reflect/is_constructible_type1.C: Adjust expected result
> 	of one is_constructible_type call.
> 	* g++.dg/init/pr43719.C: Don't expect one error.
> 	* g++.dg/init/pr25811.C: Don't expect 3 diagnostic messages,
> 	instead expect a different one for C++98 only.
> 	* g++.dg/other/anon-union2.C: Only expect one diagnostic for
> 	C++23 and older.
> 	* g++.dg/cpp0x/union1.C: Only expect 6 diagnostic messages for
> 	C++23 and older.
> 	* g++.dg/cpp0x/union4.C: Only expect 3 diagnostic messages for
> 	C++23 and older.
> 	* g++.dg/cpp0x/defaulted2.C: Only expect 2 diagnostic messages for
> 	C++23 and older.
> 
> --- gcc/c-family/c-cppbuiltin.cc.jj	2026-05-20 08:43:02.545478650 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2026-05-20 14:26:01.233439033 +0200
> @@ -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=202502L");
>   	}
>         if (flag_concepts && cxx_dialect > cxx14)
>   	cpp_define (pfile, "__cpp_concepts=202002L");
> --- gcc/cp/method.cc.jj	2026-05-20 08:43:02.566478296 +0200
> +++ gcc/cp/method.cc	2026-05-20 14:26:01.234439016 +0200
> @@ -2692,6 +2692,7 @@ walk_field_subobs (tree fields, special_
>     enum { unknown, no, yes }
>     only_dmi_mem = (sfk == sfk_constructor && TREE_CODE (ctx) == UNION_TYPE
>   		  ? unknown : no);
> +  int has_user_provided_ctor = -1;
>   
>    again:
>     for (tree field = fields; field; field = DECL_CHAIN (field))
> @@ -2771,6 +2772,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 +2849,22 @@ walk_field_subobs (tree fields, special_
>         else
>   	argtype = NULL_TREE;
>   
> +      if (cxx_dialect >= cxx26 && TREE_CODE (ctx) == UNION_TYPE)
> +	{

Please add more commentary about the semantics, including that we 
already handled the case of a DMI in a constructor.

> +	  if (sfk == sfk_constructor || sfk == sfk_inheriting_constructor)
> +	    continue;
> +
> +	  if (sfk == sfk_destructor)
> +	    {
> +	      if (!dtor_from_ctor && has_user_provided_ctor == -1)
> +		has_user_provided_ctor
> +		  = type_has_user_provided_constructor (current_class_type);
> +	      if (DECL_INITIAL (field) == NULL_TREE
> +		  && (dtor_from_ctor || !has_user_provided_ctor))
> +		continue;
> +	    }
> +	}
> +
>         rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
>   
>         process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
> --- gcc/cp/class.cc.jj	2026-05-20 08:43:02.551478549 +0200
> +++ gcc/cp/class.cc	2026-05-20 14:26:01.235439000 +0200
> @@ -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.  */

Before C++26, a non-trivial variant member ctor makes t's deleted, so I 
guess triviality never mattered?

> +	  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)
> --- gcc/testsuite/g++.dg/DRs/dr2581-1.C.jj	2026-05-20 08:43:02.577478111 +0200
> +++ gcc/testsuite/g++.dg/DRs/dr2581-1.C	2026-05-20 14:26:01.244438851 +0200
> @@ -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 } }
> --- gcc/testsuite/g++.dg/DRs/dr2581-2.C.jj	2026-05-20 08:43:02.577478111 +0200
> +++ gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-05-20 14:26:01.244751680 +0200
> @@ -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 202502L		// { 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 } }
> --- gcc/testsuite/g++.dg/cpp0x/union1.C.jj	2026-03-27 10:17:15.542307763 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/union1.C	2026-05-20 14:26:01.248075538 +0200
> @@ -14,7 +14,7 @@ union B
>     A a;				// { dg-error "union member" }
>   };
>   
> -B b;				// { dg-error "B::B\\(\\)" "B::B" }
> +B b;				// { dg-error "B::B\\(\\)" "B::B" { target c++23_down } }
>   B b2(b);			// { dg-error "B::B\\(const B&\\)" "B::B" }
>   
>   struct C
> @@ -25,10 +25,10 @@ struct C
>     };
>   };
>   
> -C c;				// { dg-error "C::C\\(\\)" "C::C" }
> +C c;				// { dg-error "C::C\\(\\)" "C::C" { target c++23_down } }
>   C c2(c);			// { dg-error "C::C\\(const C&\\)" "C::C" }
>   
> -// { dg-error "B::~B" "B::~B" { target *-*-* } 17 }
> -// { dg-error "B::~B" "B::~B" { target *-*-* } 18 }
> -// { dg-error "C::~C" "C::~C" { target *-*-* } 28 }
> -// { dg-error "C::~C" "C::~C" { target *-*-* } 29 }
> +// { dg-error "B::~B" "B::~B" { target c++23_down } 17 }
> +// { dg-error "B::~B" "B::~B" { target c++23_down } 18 }
> +// { dg-error "C::~C" "C::~C" { target c++23_down } 28 }
> +// { dg-error "C::~C" "C::~C" { target c++23_down } 29 }
> --- gcc/testsuite/g++.dg/cpp0x/union4.C.jj	2026-03-27 10:17:15.542307763 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/union4.C	2026-05-20 14:26:01.248438784 +0200
> @@ -3,15 +3,15 @@
>   
>   struct SFoo
>   {
> -  SFoo() =delete;		// { dg-message "declared" }
> +  SFoo() =delete;		// { dg-message "declared" "" { target c++23_down } }
>   };
>   
> -union UFoo			// { dg-error "deleted" }
> +union UFoo			// { dg-error "deleted" "" { target c++23_down } }
>   {
>     SFoo foo;
>   };
>   
>   int main()
>   {
> -  UFoo();			// { dg-error "deleted" }
> +  UFoo();			// { dg-error "deleted" "" { target c++23_down } }
>   }
> --- gcc/testsuite/g++.dg/cpp0x/defaulted2.C.jj	2026-03-27 10:17:15.446309330 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/defaulted2.C	2026-05-20 14:26:01.248646182 +0200
> @@ -55,7 +55,7 @@ G::G() = default;
>   
>   union U
>   {
> -  G g;				// { dg-error "union member.*non-trivial" }
> +  G g;				// { dg-error "union member.*non-trivial" "" { target c++23_down } }
>   };
>   
>   int main()
> @@ -63,7 +63,7 @@ int main()
>     F f;
>     F f2(f);			// { dg-error "use" }
>     const B* b = new const B;		// { dg-error "uninitialized const" }
> -  U u;				// { dg-error "deleted" }
> +  U u;				// { dg-error "deleted" "" { target c++23_down } }
>   }
>   
>   // { dg-prune-output "implicitly deleted because" }
> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2026-05-20 08:43:02.579478077 +0200
> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2026-05-20 14:26:01.245053195 +0200
> @@ -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 != 202502
> +#  error "__cpp_trivial_union != 202502"
> +#endif
> --- gcc/testsuite/g++.dg/cpp26/trivial-union1.C.jj	2026-05-20 22:41:11.373261717 +0200
> +++ gcc/testsuite/g++.dg/cpp26/trivial-union1.C	2026-05-21 09:07:54.724321137 +0200
> @@ -0,0 +1,133 @@
> +// P3074R7 - trivial unions (was std::uninitialized<T>)
> +// { 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
> +union M { M (); int a; int b; };
> +static_assert (std::is_default_constructible <M>::value, "");
> +static_assert (!std::is_trivially_default_constructible <M>::value, "");
> +static_assert (std::is_destructible <M>::value, "");
> +static_assert (std::is_trivially_destructible <M>::value, "");
> +union N { N (); int a; G b; };
> +static_assert (!std::is_default_constructible <N>::value, "");
> +static_assert (!std::is_trivially_default_constructible <N>::value, "");
> +static_assert (!std::is_destructible <N>::value, "");
> +static_assert (!std::is_trivially_destructible <N>::value, "");
> +struct O { O (); union { int a; int b; }; };
> +static_assert (std::is_default_constructible <O>::value, "");
> +static_assert (!std::is_trivially_default_constructible <O>::value, "");
> +static_assert (std::is_destructible <O>::value, "");
> +static_assert (std::is_trivially_destructible <O>::value, "");
> +struct P { P (); union { int a; G b; }; };
> +static_assert (!std::is_default_constructible <P>::value, "");
> +static_assert (!std::is_trivially_default_constructible <P>::value, "");
> +static_assert (!std::is_destructible <P>::value, "");
> +static_assert (!std::is_trivially_destructible <P>::value, "");
> +struct Q { Q (int); union { int a; G b; }; };
> +static_assert (!std::is_default_constructible <Q>::value, "");
> +static_assert (!std::is_trivially_default_constructible <Q>::value, "");
> +static_assert (!std::is_destructible <Q>::value, "");
> +static_assert (!std::is_trivially_destructible <Q>::value, "");
> +struct R { R () = default; R (int); union { int a; G b; }; };
> +static_assert (!std::is_default_constructible <R>::value, "");
> +static_assert (!std::is_trivially_default_constructible <R>::value, "");
> +static_assert (!std::is_destructible <R>::value, "");
> +static_assert (!std::is_trivially_destructible <R>::value, "");
> +struct S { S (int); ~S (); };
> +union T { T (); int a; int b = 42; };
> +static_assert (std::is_default_constructible <T>::value, "");
> +static_assert (!std::is_trivially_default_constructible <T>::value, "");
> +static_assert (std::is_destructible <T>::value, "");
> +static_assert (std::is_trivially_destructible <T>::value, "");
> +union U { U (); int a; S b = 42; };
> +static_assert (!std::is_default_constructible <U>::value, "");
> +static_assert (!std::is_trivially_default_constructible <U>::value, "");
> +static_assert (!std::is_destructible <U>::value, "");
> +static_assert (!std::is_trivially_destructible <U>::value, "");
> +struct V { V (); union { int a; int b = 42; }; };
> +static_assert (std::is_default_constructible <V>::value, "");
> +static_assert (!std::is_trivially_default_constructible <V>::value, "");
> +static_assert (std::is_destructible <V>::value, "");
> +static_assert (std::is_trivially_destructible <V>::value, "");
> +struct W { W (); union { int a; S b = 42; }; };
> +static_assert (!std::is_default_constructible <W>::value, "");
> +static_assert (!std::is_trivially_default_constructible <W>::value, "");
> +static_assert (!std::is_destructible <W>::value, "");
> +static_assert (!std::is_trivially_destructible <W>::value, "");
> +struct X { X (int); union { int a; S b = 42; }; };
> +static_assert (!std::is_default_constructible <X>::value, "");
> +static_assert (!std::is_trivially_default_constructible <X>::value, "");
> +static_assert (!std::is_destructible <X>::value, "");
> +static_assert (!std::is_trivially_destructible <X>::value, "");
> +struct Y { Y () = default; Y (int); union { int a; S b = 42; }; };
> +static_assert (!std::is_default_constructible <Y>::value, "");
> +static_assert (!std::is_trivially_default_constructible <Y>::value, "");
> +static_assert (!std::is_destructible <Y>::value, "");
> +static_assert (!std::is_trivially_destructible <Y>::value, "");
> --- gcc/testsuite/g++.dg/cpp26/trivial-union2.C.jj	2026-05-20 14:26:01.245359408 +0200
> +++ gcc/testsuite/g++.dg/cpp26/trivial-union2.C	2026-05-20 14:26:01.245359408 +0200
> @@ -0,0 +1,7 @@
> +// P3074R7 - trivial unions (was std::uninitialized<T>)
> +// { dg-do compile { target c++20 } }
> +
> +union U { int a, b; };
> +template<U u> class X {};
> +constexpr U make() { U u; return u; }
> +void f(X<make()>) {}
> --- gcc/testsuite/g++.dg/init/pr43719.C.jj	2026-04-29 07:45:23.186351218 +0200
> +++ gcc/testsuite/g++.dg/init/pr43719.C	2026-05-20 14:26:01.247279549 +0200
> @@ -109,7 +109,7 @@ struct Z		// { dg-error "deleted" "" { t
>     Z5 z5;
>   };
>   
> -union U // { dg-error "uninitialized" "" { target c++11 } }
> +union U
>   {
>     int const i; // { dg-message "should be initialized" }
>   };
> --- gcc/testsuite/g++.dg/init/pr25811.C.jj	2026-04-29 07:45:23.183351272 +0200
> +++ gcc/testsuite/g++.dg/init/pr25811.C	2026-05-20 14:26:01.247438801 +0200
> @@ -124,10 +124,9 @@ struct Z // { dg-error "deleted" "" { ta
>     Z5 z5;
>   };
>   
> -union U // { dg-message "implicitly deleted" "" { target c++11 } }
> -	// { dg-error "uninitialized" "" { target c++11 } .-1 }
> +union U
>   {
> -  int const i; // { dg-message "should be initialized" }
> +  int const i;
>   };
>   
>   void f1 ()
> @@ -207,5 +206,5 @@ void f15 ()
>   
>   void f16 ()
>   {
> -  new U; // { dg-error "deleted|uninitialized const member" }
> +  new U; // { dg-error "uninitialized const member in 'union U' using 'new' without new-initializer" "" { target c++98_only } }
>   }
> --- gcc/testsuite/g++.dg/other/anon-union2.C.jj	2026-03-27 10:17:16.040299637 +0100
> +++ gcc/testsuite/g++.dg/other/anon-union2.C	2026-05-20 14:26:01.247698101 +0200
> @@ -6,5 +6,5 @@ struct S {
>   };
>   
>   void f() {
> -  union { S a; };		// { dg-error "constructor|no match" }
> +  union { S a; };		// { dg-error "constructor|no match" "" { target c++23_down } }
>   }
> --- gcc/testsuite/g++.dg/reflect/trivial-union1.C.jj	2026-05-20 22:41:11.373882191 +0200
> +++ gcc/testsuite/g++.dg/reflect/trivial-union1.C	2026-05-21 09:36:03.469955381 +0200
> @@ -0,0 +1,214 @@
> +// P3074R7 - trivial unions (was std::uninitialized<T>)
> +// { 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; int 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));
> +union N { N (); int a; G b; };
> +static_assert (!is_default_constructible_type (^^N));
> +static_assert (!is_trivially_default_constructible_type (^^N));
> +static_assert (!is_destructible_type (^^N));
> +static_assert (!is_trivially_destructible_type (^^N));
> +constexpr auto Nctor = (members_of (^^N, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Ndtor = (members_of (^^N, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Nctor) && !is_deleted (Nctor));
> +static_assert (is_defaulted (Ndtor) && is_deleted (Ndtor));
> +struct O { O (); union { int a; int 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));
> +struct P { P (); union { int a; G b; }; };
> +static_assert (!is_default_constructible_type (^^P));
> +static_assert (!is_trivially_default_constructible_type (^^P));
> +static_assert (!is_destructible_type (^^P));
> +static_assert (!is_trivially_destructible_type (^^P));
> +constexpr auto Pctor = (members_of (^^P, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Pdtor = (members_of (^^P, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Pctor) && !is_deleted (Pctor));
> +static_assert (is_defaulted (Pdtor) && is_deleted (Pdtor));
> +struct Q { Q (int); union { int a; G b; }; };
> +static_assert (!is_default_constructible_type (^^Q));
> +static_assert (!is_trivially_default_constructible_type (^^Q));
> +static_assert (!is_destructible_type (^^Q));
> +static_assert (!is_trivially_destructible_type (^^Q));
> +static_assert ((members_of (^^Q, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ()).size () == 0);
> +constexpr auto Qdtor = (members_of (^^Q, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (is_defaulted (Qdtor) && is_deleted (Qdtor));
> +struct R { R () = default; R (int); union { int a; G b; }; };
> +static_assert (!is_default_constructible_type (^^R));
> +static_assert (!is_trivially_default_constructible_type (^^R));
> +static_assert (!is_destructible_type (^^R));
> +static_assert (!is_trivially_destructible_type (^^R));
> +constexpr auto Rctor = (members_of (^^R, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Rdtor = (members_of (^^R, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (is_defaulted (Rctor) && !is_deleted (Rctor));
> +static_assert (is_defaulted (Rdtor) && is_deleted (Rdtor));
> +struct S { S (int); ~S (); };
> +union T { T (); int a; int b = 42; };
> +static_assert (is_default_constructible_type (^^T));
> +static_assert (!is_trivially_default_constructible_type (^^T));
> +static_assert (is_destructible_type (^^T));
> +static_assert (is_trivially_destructible_type (^^T));
> +constexpr auto Tctor = (members_of (^^T, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Tdtor = (members_of (^^T, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Tctor) && !is_deleted (Tctor));
> +static_assert (is_defaulted (Tdtor) && !is_deleted (Tdtor));
> +union U { U (); int a; S b = 42; };
> +static_assert (!is_default_constructible_type (^^U));
> +static_assert (!is_trivially_default_constructible_type (^^U));
> +static_assert (!is_destructible_type (^^U));
> +static_assert (!is_trivially_destructible_type (^^U));
> +constexpr auto Uctor = (members_of (^^U, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Udtor = (members_of (^^U, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Uctor) && !is_deleted (Uctor));
> +static_assert (is_defaulted (Udtor) && is_deleted (Udtor));
> +struct V { V (); union { int a; int b = 42; }; };
> +static_assert (is_default_constructible_type (^^V));
> +static_assert (!is_trivially_default_constructible_type (^^V));
> +static_assert (is_destructible_type (^^V));
> +static_assert (is_trivially_destructible_type (^^V));
> +constexpr auto Vctor = (members_of (^^V, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Vdtor = (members_of (^^V, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Vctor) && !is_deleted (Vctor));
> +static_assert (is_defaulted (Vdtor) && !is_deleted (Vdtor));
> +struct W { W (); union { int a; S b = 42; }; };
> +static_assert (!is_default_constructible_type (^^W));
> +static_assert (!is_trivially_default_constructible_type (^^W));
> +static_assert (!is_destructible_type (^^W));
> +static_assert (!is_trivially_destructible_type (^^W));
> +constexpr auto Wctor = (members_of (^^W, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Wdtor = (members_of (^^W, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (Wctor) && !is_deleted (Wctor));
> +static_assert (is_defaulted (Wdtor) && is_deleted (Wdtor));
> +struct X { X (int); union { int a; S b = 42; }; };
> +static_assert (!is_default_constructible_type (^^X));
> +static_assert (!is_trivially_default_constructible_type (^^X));
> +static_assert (!is_destructible_type (^^X));
> +static_assert (!is_trivially_destructible_type (^^X));
> +static_assert ((members_of (^^X, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ()).size () == 0);
> +constexpr auto Xdtor = (members_of (^^X, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (is_defaulted (Xdtor) && is_deleted (Xdtor));
> +struct Y { Y () = default; Y (int); union { int a; S b = 42; }; };
> +static_assert (!is_default_constructible_type (^^Y));
> +static_assert (!is_trivially_default_constructible_type (^^Y));
> +static_assert (!is_destructible_type (^^Y));
> +static_assert (!is_trivially_destructible_type (^^Y));
> +constexpr auto Yctor = (members_of (^^Y, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto Ydtor = (members_of (^^Y, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (is_defaulted (Yctor) && !is_deleted (Yctor));
> +static_assert (is_defaulted (Ydtor) && is_deleted (Ydtor));
> +union AA { AA (); int a; long b; };
> +static_assert (is_default_constructible_type (^^AA));
> +static_assert (!is_trivially_default_constructible_type (^^AA));
> +static_assert (is_destructible_type (^^AA));
> +static_assert (is_trivially_destructible_type (^^AA));
> +constexpr auto AActor = (members_of (^^AA, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto AAdtor = (members_of (^^AA, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (AActor) && !is_deleted (AActor));
> +static_assert (is_defaulted (AAdtor) && !is_deleted (AAdtor));
> +struct AB { AB () = default; AB (const AB &) = default; int a; ~AB (); };
> +union AC { AC (); int a; AB b; };
> +static_assert (!is_default_constructible_type (^^AC));
> +static_assert (!is_trivially_default_constructible_type (^^AC));
> +static_assert (!is_destructible_type (^^AC));
> +static_assert (!is_trivially_destructible_type (^^AC));
> +constexpr auto ACctor = (members_of (^^AC, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0];
> +constexpr auto ACdtor = (members_of (^^AC, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0];
> +static_assert (!is_defaulted (ACctor) && !is_deleted (ACctor));
> +static_assert (is_defaulted (ACdtor) && is_deleted (ACdtor));
> --- gcc/testsuite/g++.dg/reflect/type_trait6.C.jj	2026-05-20 08:43:02.581478043 +0200
> +++ gcc/testsuite/g++.dg/reflect/type_trait6.C	2026-05-20 14:26:01.246323664 +0200
> @@ -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));
> --- gcc/testsuite/g++.dg/reflect/is_constructible_type1.C.jj	2026-05-20 08:43:02.580478060 +0200
> +++ gcc/testsuite/g++.dg/reflect/is_constructible_type1.C	2026-05-20 14:26:01.246691288 +0200
> @@ -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 }));
> 
> 
> 	Jakub
> 



More information about the Libstdc++ mailing list