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

Jakub Jelinek jakub@redhat.com
Wed May 20 08:43:47 GMT 2026


Hi!

The following patch attempts to implement the C++26
P3074R7 - trivial unions (was std::uninitialized<T>)
P3726R2 - Adjustments to Union Lifetime Rules
papers and proposed resolution of
CWG3189 - Implicitly deleted destructors for union-like classes
with the exception of the
  #define __cpp_lib_constexpr_inplace_vector 2025XXL // also in <inplace_vector>
addition and possibly needed <inplace_vector> changes (will defer that to
Jonathan / Tomasz).

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.

For std::start_lifetime I've added a FE builtin which unlike the standard
function template takes a pointer rather than reference, because it needs to
be type-generic and for (...) it would attempt to pass a copy of the
referenced object rather than the reference.  As I didn't want to duplicate
most of the cxx_eval_store_expression function for its constant evaluation,
I've added an argument to that function and handle it as magic MODIFY_EXPR
that in some cases doesn't do anything, but in other cases stores the
CONSTRUCTOR_NO_CLEARING empty CONSTRUCTOR.  The P3726R2 description of
start_lifetime doesn't mention what to do when it is passed a reference to
something which is not a constant expression (sometimes it could be
within its lifetime, sometimes (e.g. when there is a union) we can't tell
at constant evaluation time), or when it is one that didn't start lifetime
during the evaluation of the core constant expression.  The patch
assumes it is valid to only handle stuff where the object actually could
change lifetime.  Another thing is const qualification.  I believe even
just changing the active union member is a change and so inappropriate in
something that is const (unless during construction thereof), so the patch
won't diagnose anything if all the right union members are active already,
but if it would need to make some other union member active in something
that is const, it will diagnose it.

In reduced_constant_expression_p the patch also implements the second
part of P3726R2, the changes to union elemental subobject handling, where
it ignores holes for C++26 in arrays (possibly multi-dimensional) directly
nested inside of union.

There is one xfail in trivial-union6.C testcase,
struct A { int a[2]; };
constexpr A fA () { A a; a.a[0] = 1; return a; }
constexpr A a = fA ();         // { dg-error "is not a constant expression" "" { xfail *-*-* } }
which is preexisting issue not actually related to unions, clang++ rejects
this because there are holes in it (and for C++26 it is not union elemental
subobject), but we happily just clear it in
  if (TREE_CODE (result) == CONSTRUCTOR
      && (cxx_dialect < cxx20
          || !DECL_CONSTRUCTOR_P (fun)))
    clear_no_implicit_zero (result);
with no diagnostics.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-05-20  Jakub Jelinek  <jakub@redhat.com>

gcc/c-family/
	* c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine
	__cpp_trivial_union to 202603L.
gcc/cp/
	* method.cc: Implement C++26 P3074R7 - trivial unions (was
	std::uninitialized<T>), P3726R2 - Adjustments to Union Lifetime Rules
	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.
	* cp-tree.h (enum cp_built_in_function): Add
	CP_BUILT_IN_START_LIFETIME.
	(check_builtin_start_lifetime): Declare.
	(reduced_constant_expression_p): Add a bool argument defaulted to
	false.
	* tree.cc (builtin_valid_in_constant_expr_p): Handle
	CP_BUILT_IN_START_LIFETIME.
	* decl.cc (cxx_init_decl_processing): Create __builtin_start_lifetime
	decl.
	* cp-gimplify.cc (cp_gimplify_expr): Handle
	CP_BUILT_IN_START_LIFETIME.
	* semantics.cc (check_builtin_start_lifetime): New function.
	* constexpr.cc (cxx_eval_start_lifetime): New function.
	(cxx_eval_builtin_function_call): Handle CP_BUILT_IN_START_LIFETIME.
	(reduced_constant_expression_p): Add union_elemental_subobject
	argument and propagate it to recursive calls.  If set, ignore holes
	in array initializers.
	(cxx_eval_store_expression): Add start_lifetime argument.  Don't
	preevaluate init in tha tcase, return early if !seen_union after
	loop around the possibly nested ARRAY_REFs/COMPONENT_REFs etc.
	or when all union members are already active.
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/cpp26/trivial-union3.C: New test.
	* g++.dg/cpp26/trivial-union4.C: New test.
	* g++.dg/cpp26/trivial-union5.C: New test.
	* g++.dg/cpp26/trivial-union6.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.
libstdc++-v3/
	* include/bits/version.def (start_lifetime): New.
	* include/bits/version.h: Regenerate.
	* include/std/memory: Define __glibcxx_want_start_lifetime before
	including bits/version.h.
	(std::start_lifetime): New function template.
	* src/c++23/std.cc.in: Add export std::start_lifetime.

--- gcc/c-family/c-cppbuiltin.cc.jj	2026-05-19 09:11:42.796347298 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2026-05-19 14:49:03.999623920 +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=202603L");
 	}
       if (flag_concepts && cxx_dialect > cxx14)
 	cpp_define (pfile, "__cpp_concepts=202002L");
--- gcc/cp/method.cc.jj	2026-05-19 09:11:42.825346810 +0200
+++ gcc/cp/method.cc	2026-05-19 14:49:04.000032882 +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)
+	{
+	  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-19 09:11:42.800347231 +0200
+++ gcc/cp/class.cc	2026-05-19 14:49:04.000697204 +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.  */
+	  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/cp/cp-tree.h.jj	2026-05-19 09:11:42.814346995 +0200
+++ gcc/cp/cp-tree.h	2026-05-19 17:24:56.329431575 +0200
@@ -7245,6 +7245,7 @@ enum cp_built_in_function {
   CP_BUILT_IN_CONSTEXPR_DIAG,
   CP_BUILT_IN_CURRENT_EXCEPTION,
   CP_BUILT_IN_UNCAUGHT_EXCEPTIONS,
+  CP_BUILT_IN_START_LIFETIME,
   CP_BUILT_IN_LAST
 };
 
@@ -8665,6 +8666,7 @@ extern tree fold_builtin_is_correspondin
 extern bool pointer_interconvertible_base_of_p	(tree, tree, bool = false);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *);
 extern tree fold_builtin_is_string_literal	(location_t, int, tree *);
+extern bool check_builtin_start_lifetime	(location_t, int, tree *, tsubst_flags_t);
 extern tree finish_structured_binding_size	(location_t, tree, tsubst_flags_t);
 extern tree finish_trait_expr			(location_t, enum cp_trait_kind, tree, tree);
 extern tree finish_trait_type			(enum cp_trait_kind, tree, tree, tsubst_flags_t);
@@ -9434,7 +9436,7 @@ extern tree fold_non_dependent_init		(tr
 						 bool = false, tree = NULL_TREE);
 extern tree fold_simple				(tree);
 extern tree fold_to_constant			(tree);
-extern bool reduced_constant_expression_p       (tree, tree = NULL_TREE);
+extern bool reduced_constant_expression_p       (tree, tree = NULL_TREE, bool = false);
 extern bool is_instantiation_of_constexpr       (tree);
 extern bool var_in_constexpr_fn                 (tree);
 extern bool var_in_maybe_constexpr_fn           (tree);
--- gcc/cp/tree.cc.jj	2026-05-19 09:11:42.827346776 +0200
+++ gcc/cp/tree.cc	2026-05-19 14:49:04.002359526 +0200
@@ -574,6 +574,7 @@ builtin_valid_in_constant_expr_p (const_
 	  case CP_BUILT_IN_CONSTEXPR_DIAG:
 	  case CP_BUILT_IN_CURRENT_EXCEPTION:
 	  case CP_BUILT_IN_UNCAUGHT_EXCEPTIONS:
+	  case CP_BUILT_IN_START_LIFETIME:
 	    return true;
 	  default:
 	    break;
--- gcc/cp/decl.cc.jj	2026-05-19 09:11:42.818346928 +0200
+++ gcc/cp/decl.cc	2026-05-19 14:49:04.003068157 +0200
@@ -5663,6 +5663,13 @@ cxx_init_decl_processing (void)
 			       BUILT_IN_FRONTEND, NULL, NULL_TREE);
   set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
 
+  tree void_vaftype = build_varargs_function_type_list (boolean_type_node,
+							NULL_TREE);
+  decl = add_builtin_function ("__builtin_start_lifetime",
+			       void_vaftype, CP_BUILT_IN_START_LIFETIME,
+			       BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
--- gcc/cp/cp-gimplify.cc.jj	2026-05-19 09:11:42.813347012 +0200
+++ gcc/cp/cp-gimplify.cc	2026-05-19 16:47:38.464644177 +0200
@@ -1015,6 +1015,14 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 	      case CP_BUILT_IN_CONSTEXPR_DIAG:
 		*expr_p = void_node;
 		break;
+	      case CP_BUILT_IN_START_LIFETIME:
+		check_builtin_start_lifetime (EXPR_LOCATION (*expr_p),
+					      call_expr_nargs (*expr_p),
+					      call_expr_nargs (*expr_p)
+					      ? &CALL_EXPR_ARG (*expr_p, 0)
+					      : NULL,
+					      tf_warning_or_error);
+		*expr_p = void_node;
 	      default:
 		break;
 	      }
--- gcc/cp/semantics.cc.jj	2026-05-19 14:48:47.628341810 +0200
+++ gcc/cp/semantics.cc	2026-05-19 14:49:04.005338408 +0200
@@ -13984,6 +13984,53 @@ fold_builtin_is_string_literal (location
   return boolean_true_node;
 }
 
+/* Perform __builtin_start_lifetime call checking, return false
+   when errors are reported.  */
+
+bool
+check_builtin_start_lifetime (location_t loc, int nargs, tree *args,
+			      tsubst_flags_t complain)
+{
+  /* Unless users call the builtin directly, the following 2 checks should be
+     ensured from std::start_lifetime template.  */
+  if (nargs != 1)
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_start_lifetime%> needs a single "
+		  "argument");
+      return false;
+    }
+  tree arg = args[0];
+  if (error_operand_p (arg))
+    return false;
+  tree ptype = TREE_TYPE (arg);
+  if (!POINTER_TYPE_P (ptype))
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_start_lifetime%> argument type %qT "
+		  "is not pointer type", ptype);
+      return false;
+    }
+  tree type = TREE_TYPE (ptype);
+  if (!complete_type_or_else (type, NULL_TREE))
+    return false;
+  if (!CP_AGGREGATE_TYPE_P (type))
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_start_lifetime%> argument type %qT "
+		  "is not a pointer to aggregate type", ptype);
+      return false;
+    }
+  if (!implicit_lifetime_type_p (type))
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_start_lifetime%> argument type %qT "
+		  "is not a pointer to implicit-lifetime type", ptype);
+      return false;
+    }
+  return true;
+}
+
 /* [basic.types] 8.  True iff TYPE is an object type.  */
 
 static bool
--- gcc/cp/constexpr.cc.jj	2026-05-19 09:11:48.004259554 +0200
+++ gcc/cp/constexpr.cc	2026-05-19 17:35:17.969127198 +0200
@@ -2456,6 +2456,49 @@ cxx_eval_constexpr_diag (const constexpr
   return void_node;
 }
 
+static tree cxx_eval_store_expression (const constexpr_ctx *, tree,
+				       value_cat, bool *, bool *,
+				       tree *, bool = false);
+
+/* Attempt to evaluate T which represents a call to
+   __builtin_start_lifetime.  */
+
+static tree
+cxx_eval_start_lifetime (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
+			 bool *overflow_p, tree *jump_target)
+{
+  location_t loc = EXPR_LOCATION (t);
+  if (!check_builtin_start_lifetime (loc, call_expr_nargs (t),
+				     call_expr_nargs (t)
+				     ? &CALL_EXPR_ARG (t, 0) : NULL,
+				     ctx->quiet ? tf_none
+				     : tf_warning_or_error))
+    {
+      *non_constant_p = true;
+      return t;
+    }
+  tree arg = CALL_EXPR_ARG (t, 0);
+  arg = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (arg)), arg);
+  arg = cxx_eval_constant_expression (ctx, arg, vc_glvalue,
+				      non_constant_p, overflow_p,
+				      jump_target);
+  if (*jump_target)
+    return NULL_TREE;
+  if (*non_constant_p)
+    return t;
+  tree ctor = build_constructor (TREE_TYPE (arg), NULL);
+  CONSTRUCTOR_NO_CLEARING (ctor) = 1;
+  tree modexpr = build2 (MODIFY_EXPR, TREE_TYPE (arg), arg, ctor);
+  arg = cxx_eval_store_expression (ctx, modexpr, vc_discard, non_constant_p,
+				   overflow_p, jump_target, true);
+  ggc_free (modexpr);
+  if (*jump_target)
+    return NULL_TREE;
+  if (*non_constant_p)
+    return t;
+  return void_node;
+}
+
 /* Attempt to evaluate T which represents a call to a builtin function.
    We assume here that all builtin functions evaluate to scalar types
    represented by _CST nodes.  */
@@ -2529,6 +2572,10 @@ cxx_eval_builtin_function_call (const co
 	return cxx_eval_constexpr_diag (ctx, t, non_constant_p, overflow_p,
 					jump_target);
 
+      case CP_BUILT_IN_START_LIFETIME:
+        return cxx_eval_start_lifetime (ctx, t, non_constant_p, overflow_p,
+					jump_target);
+
       default:
 	break;
       }
@@ -4708,7 +4755,8 @@ cxx_eval_call_expression (const constexp
    FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
-reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */)
+reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */,
+			       bool union_elemental_subobject /* = false */)
 {
   if (t == NULL_TREE)
     return false;
@@ -4732,7 +4780,10 @@ reduced_constant_expression_p (tree t, t
 	return false;
       if (CONSTRUCTOR_NO_CLEARING (t))
 	{
-	  if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+	  if (union_elemental_subobject
+	      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+	    field = NULL_TREE;
+	  else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
 	    {
 	      /* There must be a valid constant initializer at every array
 		 index.  */
@@ -4766,12 +4817,25 @@ reduced_constant_expression_p (tree t, t
 		 active member has no constituent values, so all constituent
 		 values are constant.  */
 	      field = NULL_TREE;
+	      union_elemental_subobject = cxx_dialect >= cxx26;
 	    }
 	  else
-	    field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t)));
+	    {
+	      field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t)));
+	      union_elemental_subobject = false;
+	    }
 	}
       else
-	field = NULL_TREE;
+	{
+	  field = NULL_TREE;
+	  if (cxx_dialect >= cxx26)
+	    {
+	      if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE)
+		union_elemental_subobject = true;
+	      else if (TREE_CODE (TREE_TYPE (t)) != ARRAY_TYPE)
+		union_elemental_subobject = false;
+	    }
+	}
       for (auto &e: CONSTRUCTOR_ELTS (t))
 	{
 	  /* If VAL is null, we're in the middle of initializing this
@@ -4781,7 +4845,8 @@ reduced_constant_expression_p (tree t, t
 					       && (TREE_CODE (e.index)
 						   == FIELD_DECL))
 					      ? DECL_SIZE (e.index)
-					      : NULL_TREE))
+					      : NULL_TREE,
+					      union_elemental_subobject))
 	    return false;
 	  /* We want to remove initializers for empty fields in a struct to
 	     avoid confusing output_constructor.  */
@@ -7813,7 +7878,7 @@ static tree
 cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 			   value_cat lval,
 			   bool *non_constant_p, bool *overflow_p,
-			   tree *jump_target)
+			   tree *jump_target, bool start_lifetime/*=false*/)
 {
   constexpr_ctx new_ctx = *ctx;
 
@@ -7826,7 +7891,7 @@ cxx_eval_store_expression (const constex
 
   tree type = TREE_TYPE (target);
   bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR;
-  if (preeval && !TREE_CLOBBER_P (init))
+  if (preeval && !TREE_CLOBBER_P (init) && !start_lifetime)
     {
       /* Ignore var = .DEFERRED_INIT (); for now, until PR121965 is fixed.  */
       if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
@@ -8033,6 +8098,11 @@ cxx_eval_store_expression (const constex
       const_object_being_modified = NULL_TREE;
     }
 
+  /* For __builtin_start_lifetime, if no union is involved, no need to
+     change anything.  */
+  if (start_lifetime && !seen_union)
+    return void_node;
+
   type = TREE_TYPE (object);
   bool no_zero_init = true;
   bool zero_padding_bits = false;
@@ -8166,12 +8236,13 @@ cxx_eval_store_expression (const constex
 			  index);
 	      *non_constant_p = true;
 	    }
-	  else if (!is_access_expr
-		   || (TREE_CLOBBER_P (init)
-		       && CLOBBER_KIND (init) >= CLOBBER_OBJECT_END)
-		   || (TREE_CODE (t) == MODIFY_EXPR
-		       && CLASS_TYPE_P (inner)
-		       && !type_has_non_deleted_trivial_default_ctor (inner)))
+	  else if ((!is_access_expr
+		    || (TREE_CLOBBER_P (init)
+			&& CLOBBER_KIND (init) >= CLOBBER_OBJECT_END)
+		    || (TREE_CODE (t) == MODIFY_EXPR
+			&& CLASS_TYPE_P (inner)
+			&& !type_has_non_deleted_trivial_default_ctor (inner)))
+		   && !start_lifetime)
 	    {
 	      /* Diagnose changing active union member after initialization
 		 without a valid member access expression, as described in
@@ -8217,6 +8288,10 @@ cxx_eval_store_expression (const constex
 	    }
 	  no_zero_init = true;
 	}
+      /* If this is the innermost union and we aren't changing active member
+	 of it during start_lifetime, we don't need to change anything.  */
+      else if (code == UNION_TYPE && start_lifetime && seen_union == 1)
+	return void_node;
 
       /* Ending the lifetime of the active union member means the union no
 	 longer has an active member.  */
--- gcc/testsuite/g++.dg/DRs/dr2581-1.C.jj	2026-05-19 09:11:42.840346557 +0200
+++ gcc/testsuite/g++.dg/DRs/dr2581-1.C	2026-05-19 14:49:04.007045168 +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-19 09:11:42.840346557 +0200
+++ gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-05-19 14:49:04.007165066 +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 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 } }
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2026-05-19 09:11:42.844346490 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2026-05-19 14:49:04.007326066 +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 != 202603
+#  error "__cpp_trivial_union != 202603"
+#endif
--- gcc/testsuite/g++.dg/cpp26/trivial-union1.C.jj	2026-05-19 14:49:04.007463254 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union1.C	2026-05-19 14:49:04.007463254 +0200
@@ -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
--- gcc/testsuite/g++.dg/cpp26/trivial-union2.C.jj	2026-05-19 15:40:23.361611939 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union2.C	2026-05-19 16:05:33.305490645 +0200
@@ -0,0 +1,8 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { 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/cpp26/trivial-union3.C.jj	2026-05-19 15:44:47.009211069 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union3.C	2026-05-19 15:46:55.055074118 +0200
@@ -0,0 +1,28 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++26 } }
+
+#include <memory>
+#include <string>
+
+template <typename T, size_t N>
+struct FixedVector {
+  union { T storage[N]; };
+  size_t size = 0;
+
+  constexpr FixedVector () { std::start_lifetime (storage); }
+
+  constexpr ~FixedVector() { std::destroy(storage, storage + size); }
+
+  constexpr void push_back(T const& v) { ::new (storage + size) T(v); ++size; }
+};
+
+constexpr size_t
+silly_test ()
+{
+  FixedVector <std::string, 3> v;
+  v.push_back ("some sufficiently longer string");
+  return v.size;
+}
+
+static_assert (silly_test () == 1);
--- gcc/testsuite/g++.dg/cpp26/trivial-union4.C.jj	2026-05-19 16:38:43.464464194 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union4.C	2026-05-19 16:41:51.769423861 +0200
@@ -0,0 +1,63 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++26 } }
+
+namespace std {
+  template <class T>
+  constexpr void
+  start_lifetime (T &r) noexcept
+  {
+    __builtin_start_lifetime (__builtin_addressof (r));
+  }
+}
+
+struct S { int a; };
+union U { int a; S b[3]; };
+union V { int a; U b[2]; };
+
+consteval
+{
+  S s;
+  s.a = 42;
+  std::start_lifetime (s);
+  if (s.a != 42)
+    throw 1;
+  U u;
+  u.b[0].a = 42;
+  std::start_lifetime (u.b);
+  std::start_lifetime (u.b[0]);
+  std::start_lifetime (u.b[2]);
+  u.a = 42;
+  std::start_lifetime (u.b);
+  std::start_lifetime (u.b[0]);
+  std::start_lifetime (u.b[1]);
+  V v;
+  v.a = 42;
+  std::start_lifetime (v.b);
+  std::start_lifetime (v.b[1]);
+  v.b[1].a = 42;
+  std::start_lifetime (v.b[1].b);
+  std::start_lifetime (v.b[1].b[1]);
+  v.b[1].b[1].a = 43;
+  std::start_lifetime (v.b);
+  std::start_lifetime (v.b[1].b[1]);
+  if (v.b[1].b[1].a != 43)
+    throw 2;
+  const S cs = { 42 };
+  std::start_lifetime (cs);
+  if (cs.a != 42)
+    throw 3;
+  const U cu = { .b = { { 41 }, { 42 }, { 43 } } };
+  std::start_lifetime (cu.b);
+  std::start_lifetime (cu.b[0]);
+  std::start_lifetime (cu.b[2]);
+  if (cu.b[2].a != 43)
+    throw 4;
+  const V cv = { .b = { { .b = { { 44 } } } } };
+  std::start_lifetime (cv.b);
+  std::start_lifetime (cv.b[0]);
+  std::start_lifetime (cv.b[0].b);
+  std::start_lifetime (cv.b[0].b[0]);
+  if (cv.b[0].b[0].a != 44)
+    throw 5;
+}
--- gcc/testsuite/g++.dg/cpp26/trivial-union5.C.jj	2026-05-19 16:42:08.294148384 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union5.C	2026-05-19 17:16:15.760061082 +0200
@@ -0,0 +1,37 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++26 } }
+
+struct S { int a; };
+S a = { 5 };
+struct T { constexpr T () : a (42) {} int a; };
+struct R { int a; constexpr ~R () {} };
+union U { int a; S b[2]; };
+
+consteval { int a = 5; __builtin_start_lifetime (&a); }		// { dg-error "'__builtin_start_lifetime' argument type 'int\\\*' is not a pointer to aggregate type" }
+consteval { __builtin_start_lifetime (&a); };			// { dg-error "modification of 'a' from outside current evaluation is not a constant expression" }
+consteval { __builtin_start_lifetime (); };			// { dg-error "'__builtin_start_lifetime' needs a single argument" }
+consteval { S a = { 6 }; __builtin_start_lifetime (&a, &a); };	// { dg-error "'__builtin_start_lifetime' needs a single argument" }
+consteval { __builtin_start_lifetime (42); };			// { dg-error "'__builtin_start_lifetime' argument type 'int' is not pointer type" }
+consteval { T t; __builtin_start_lifetime (&t); }		// { dg-error "'__builtin_start_lifetime' argument type 'T\\\*' is not a pointer to aggregate type" }
+consteval { R r; __builtin_start_lifetime (&r); }		// { dg-error "'__builtin_start_lifetime' argument type 'R\\\*' is not a pointer to implicit-lifetime type" }
+consteval { const U u = { .a = 42 }; __builtin_start_lifetime (&u.a); }	// { dg-error "'__builtin_start_lifetime' argument type 'const int\\\*' is not a pointer to aggregate type" }
+consteval { const U u = { .a = 42 }; __builtin_start_lifetime (&u.b); }	// { dg-error "modifying a const object 'u.U::b' is not allowed in a constant expression" }
+
+void
+foo ()
+{
+  S s;
+  __builtin_start_lifetime (&s);
+  int a = 5;
+  __builtin_start_lifetime (&a);		// { dg-error "'__builtin_start_lifetime' argument type 'int\\\*' is not a pointer to aggregate type" }
+  __builtin_start_lifetime (&::a);
+  __builtin_start_lifetime ();			// { dg-error "'__builtin_start_lifetime' needs a single argument" }
+  S b = { 6 };
+  __builtin_start_lifetime (&b, &b);		// { dg-error "'__builtin_start_lifetime' needs a single argument" }
+  __builtin_start_lifetime (42);		// { dg-error "'__builtin_start_lifetime' argument type 'int' is not pointer type" }
+  T t;
+  __builtin_start_lifetime (&t);		// { dg-error "'__builtin_start_lifetime' argument type 'T\\\*' is not a pointer to aggregate type" }
+  R r;
+  __builtin_start_lifetime (&r);		// { dg-error "'__builtin_start_lifetime' argument type 'R\\\*' is not a pointer to implicit-lifetime type" }
+}
--- gcc/testsuite/g++.dg/cpp26/trivial-union6.C.jj	2026-05-19 17:54:58.353551284 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union6.C	2026-05-19 18:00:19.595221493 +0200
@@ -0,0 +1,26 @@
+// P3074R7 - trivial unions (was std::uninitialized<T>)
+// P3726R2 - Adjustments to Union Lifetime Rules
+// { dg-do compile { target c++20 } }
+
+struct A { int a[2]; };
+union B { int a; int b[2]; };
+union C { int a; int b[2][2]; };
+struct D { B a; };
+struct E { C a; };
+union F { int a; A b; };
+union G { int a; A b[2]; };
+
+constexpr A fA () { A a; a.a[0] = 1; return a; }
+constexpr A a = fA ();		// { dg-error "is not a constant expression" "" { xfail *-*-* } }
+constexpr B fB () { B a; a.b[0] = 1; return a; }
+constexpr B b = fB ();		// { dg-error "is not a constant expression" "" { target c++23_down } }
+constexpr C fC () { C a; a.b[0][0] = 1; a.b[1][0] = 2; a.b[1][1] = 3; return a; }
+constexpr C c = fC ();		// { dg-error "is not a constant expression" "" { target c++23_down } }
+constexpr D fD () { D a; a.a.b[0] = 1; return a; }
+constexpr D d = fD ();		// { dg-error "is not a constant expression" "" { target c++23_down } }
+constexpr E fE () { E a; a.a.b[0][0] = 1; a.a.b[1][0] = 2; a.a.b[1][1] = 3; return a; }
+constexpr E e = fE ();		// { dg-error "is not a constant expression" "" { target c++23_down } }
+constexpr F fF () { F a; a.b.a[0] = 1; return a; }
+constexpr F f = fF ();		// { dg-error "is not a constant expression" }
+constexpr G fG () { G a; a.b[0].a[0] = 1; return a; }
+constexpr G g = fG ();		// { dg-error "is not a constant expression" }
--- gcc/testsuite/g++.dg/reflect/trivial-union1.C.jj	2026-05-19 14:49:04.007691969 +0200
+++ gcc/testsuite/g++.dg/reflect/trivial-union1.C	2026-05-19 14:49:04.007691969 +0200
@@ -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));
--- gcc/testsuite/g++.dg/reflect/type_trait6.C.jj	2026-05-19 09:11:42.847346439 +0200
+++ gcc/testsuite/g++.dg/reflect/type_trait6.C	2026-05-19 14:49:04.007837480 +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-19 09:11:42.847346439 +0200
+++ gcc/testsuite/g++.dg/reflect/is_constructible_type1.C	2026-05-19 14:49:04.008086954 +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 }));
--- gcc/testsuite/g++.dg/init/pr43719.C.jj	2026-04-29 23:30:26.794629097 +0200
+++ gcc/testsuite/g++.dg/init/pr43719.C	2026-05-20 09:39:15.124693366 +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 23:30:26.794629097 +0200
+++ gcc/testsuite/g++.dg/init/pr25811.C	2026-05-20 09:46:05.297786524 +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	2020-01-14 20:02:46.897608126 +0100
+++ gcc/testsuite/g++.dg/other/anon-union2.C	2026-05-20 09:41:12.450717732 +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/cpp0x/union1.C.jj	2020-01-14 20:02:46.761610163 +0100
+++ gcc/testsuite/g++.dg/cpp0x/union1.C	2026-05-20 09:44:52.092019226 +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	2020-01-14 20:02:46.761610163 +0100
+++ gcc/testsuite/g++.dg/cpp0x/union4.C	2026-05-20 09:33:12.192804729 +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	2020-01-14 20:02:46.722610747 +0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted2.C	2026-05-20 09:31:00.031028888 +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" }
--- libstdc++-v3/include/bits/version.def.jj	2026-05-19 09:11:42.851346372 +0200
+++ libstdc++-v3/include/bits/version.def	2026-05-19 14:49:04.008291289 +0200
@@ -2410,6 +2410,15 @@ ftms = {
   };
 };
 
+ftms = {
+  name = start_lifetime;
+  values = {
+    v = 202603;
+    cxxmin = 26;
+    extra_cond = "__has_builtin(__builtin_start_lifetime)";
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
--- libstdc++-v3/include/bits/version.h.jj	2026-05-19 09:11:42.851346372 +0200
+++ libstdc++-v3/include/bits/version.h	2026-05-19 14:49:04.008536034 +0200
@@ -2675,4 +2675,14 @@
 #endif /* !defined(__cpp_lib_is_structural) */
 #undef __glibcxx_want_is_structural
 
+#if !defined(__cpp_lib_start_lifetime)
+# if (__cplusplus >  202302L) && (__has_builtin(__builtin_start_lifetime))
+#  define __glibcxx_start_lifetime 202603L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_start_lifetime)
+#   define __cpp_lib_start_lifetime 202603L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_start_lifetime) */
+#undef __glibcxx_want_start_lifetime
+
 #undef __glibcxx_want_all
--- libstdc++-v3/include/std/memory.jj	2026-05-19 09:11:42.854346321 +0200
+++ libstdc++-v3/include/std/memory	2026-05-19 16:08:08.038949038 +0200
@@ -126,6 +126,7 @@
 #define __glibcxx_want_transparent_operators
 #define __glibcxx_want_smart_ptr_owner_equality
 #define __glibcxx_want_allocate_at_least
+#define __glibcxx_want_start_lifetime
 #include <bits/version.h>
 
 #if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED
@@ -175,6 +176,28 @@ _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 #endif // C++11 to C++20
 
+#if __glibcxx_start_lifetime >= 202603L // C++ >= 26
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template <class _Tp>
+    constexpr void
+    start_lifetime(_Tp& __r) noexcept
+    {
+#ifdef __cpp_lib_is_implicit_lifetime
+      static_assert(std::is_implicit_lifetime_v<_Tp>);
+#endif
+#ifdef __cpp_lib_is_aggregate
+      static_assert(std::is_aggregate_v<_Tp>);
+#endif
+      __builtin_start_lifetime(__builtin_addressof(__r));
+    }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif
+
 #ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED
 // Parallel STL algorithms
 # if _PSTL_EXECUTION_POLICIES_DEFINED
--- libstdc++-v3/src/c++23/std.cc.in.jj	2026-04-22 15:03:36.487701193 +0200
+++ libstdc++-v3/src/c++23/std.cc.in	2026-05-19 16:17:58.239126126 +0200
@@ -2062,6 +2062,9 @@ export namespace std
 #ifdef __glibcxx_allocate_at_least
   using std::allocation_result;
 #endif
+#if __cpp_lib_start_lifetime
+  using std::start_lifetime;
+#endif
 }
 
 // 20.4 <memory_resource>

	Jakub



More information about the Libstdc++ mailing list