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

Jakub Jelinek jakub@redhat.com
Wed May 20 15:40:39 GMT 2026


On Wed, May 20, 2026 at 02:00:27PM +0200, Tomasz Kaminski wrote:
> I would like to see test user-provided constructors for union-like types,
> something like:
> union U  { U();  int x; X b;  }; // trivial for X = int, and deleted for T
> = G (non-trivial destructor)
> struct UL { UL(); union { int x; X b; }  }; // same
> 
> Also, for a non-default constructor and a union-like version of the above
> union U { U(int);  int x; X b;  }; // All constructors matter, not only the
> default one,
> union  U { U() = default; U(int);  int x; X b;  }; // all cosntructor
> matters, not only default,
> (above with union-like versions)

I've added some tests.

> This would be a follow-up regardless, but could you check if codegen for
> the below
> is now the same:
> FixedVector<char, 4> f() {
>   return {'a', 'b', 'c'};
> }
> FixedVector<char, 4> g() {
>   constexpr FixedVector<char, 4> result{'a', 'b', 'c'};
>   return result;
> }
> 
> (Need a FixedVector from initializer_liust constructor )
> 
> // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124121

The FixedVector doesn't have ctor from initializer_list; when using
inplace_vector as is in the headers, f gets worse code generated,
e.g. because start_lifetime_as uses inline asm.

> > +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 } }
> >
> I think it's already mentioned in the commit message, but all of the above
> should also fails in C++26 mode.
> The assigments rules were not changed so a.b[0][0] = 1, starts the whole
> array with all the elements,
> and we have uninitialized elements inside, that are in-lifetime.
> 
> To achieve the above, you should start_lifetime of the array first and only
> then assign values to its elements,
> so https://eel.is/c++draft/class.union#general-5 does not create an array
> with elements.

Haven't touched this yet.
> 
> +static_assert (!is_defaulted (Octor) && !is_deleted (Octor));
> > +static_assert (is_defaulted (Odtor) && is_deleted (Odtor));
> >
> I assume you are skipping questions about the constructor/destructor
> anonymous union inside
> union-like types on purpose?

CWG3130 says they don't exist and I plan to work on resolving that soon,
so trying to test those is pointless.

> > +#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
> >
> Could you move the declaration to libstdc++-v3/include/bits/stl_construct.h,
> (we start_lifetime_as, cosntruct_as is). We do not not want  to include
> whole header.

Moved.

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/bits/stl_construct.h (std::start_lifetime): New function
	template.
	* include/std/memory: Define __glibcxx_want_start_lifetime before
	including bits/version.h.
	* src/c++23/std.cc.in: Add export std::start_lifetime.

--- 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=202603L");
 	}
       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)
+	{
+	  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.  */
+	  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-20 08:43:07.245399375 +0200
+++ gcc/cp/cp-tree.h	2026-05-20 14:26:01.237438967 +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
 };
 
@@ -8666,6 +8667,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);
@@ -9435,7 +9437,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-20 08:43:02.572478195 +0200
+++ gcc/cp/tree.cc	2026-05-20 14:26:01.238177470 +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-20 08:43:02.564478330 +0200
+++ gcc/cp/decl.cc	2026-05-20 14:26:01.240438917 +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-20 08:43:02.554478498 +0200
+++ gcc/cp/cp-gimplify.cc	2026-05-20 14:26:01.241438900 +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-20 08:43:07.247399342 +0200
+++ gcc/cp/semantics.cc	2026-05-20 14:26:01.242438884 +0200
@@ -13989,6 +13989,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-20 08:43:07.244399392 +0200
+++ gcc/cp/constexpr.cc	2026-05-20 14:26:01.243438867 +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.  */
@@ -7817,7 +7882,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;
 
@@ -7830,7 +7895,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
@@ -8037,6 +8102,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;
@@ -8170,12 +8240,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
@@ -8221,6 +8292,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-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 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/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 != 202603
+#  error "__cpp_trivial_union != 202603"
+#endif
--- gcc/testsuite/g++.dg/cpp26/trivial-union1.C.jj	2026-05-20 14:26:01.245197174 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union1.C	2026-05-20 17:26:39.297942500 +0200
@@ -0,0 +1,93 @@
+// 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
+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, "");
--- 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,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-20 14:26:01.245486713 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union3.C	2026-05-20 14:26:01.245486713 +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-20 14:26:01.245562152 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union4.C	2026-05-20 14:26:01.245562152 +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-20 14:26:01.245655416 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union5.C	2026-05-20 14:26:01.245655416 +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-20 14:26:01.245756124 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-union6.C	2026-05-20 14:26:01.245756124 +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/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 14:26:01.245919804 +0200
+++ gcc/testsuite/g++.dg/reflect/trivial-union1.C	2026-05-20 14:26:01.245919804 +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-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 }));
--- libstdc++-v3/include/bits/version.def.jj	2026-05-20 08:43:02.584477993 +0200
+++ libstdc++-v3/include/bits/version.def	2026-05-20 14:26:01.249947842 +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-20 08:43:02.584477993 +0200
+++ libstdc++-v3/include/bits/version.h	2026-05-20 14:26:01.250438751 +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/bits/stl_construct.h.jj	2026-03-27 10:17:22.883187971 +0100
+++ libstdc++-v3/include/bits/stl_construct.h	2026-05-20 14:30:52.025619611 +0200
@@ -404,6 +404,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 #endif // C++23
 
+#if __glibcxx_start_lifetime >= 202603L // C++ >= 26
+  template <typename _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));
+    }
+#endif // C++26
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
--- libstdc++-v3/include/std/memory.jj	2026-05-20 08:43:02.586477959 +0200
+++ libstdc++-v3/include/std/memory	2026-05-20 14:28:05.835373954 +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
--- libstdc++-v3/src/c++23/std.cc.in.jj	2026-05-20 08:43:02.591477875 +0200
+++ libstdc++-v3/src/c++23/std.cc.in	2026-05-20 14:26:01.251438735 +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