[PATCH] c++, libstdc++, v2: Implement C++26 P3726R2 - Adjustments to Union Lifetime Rules [PR125845]

Jason Merrill jason@redhat.com
Tue Sep 8 19:39:09 GMT 2026


On 9/7/26 7:50 AM, Jakub Jelinek wrote:
> On Fri, Sep 04, 2026 at 04:44:22PM -0400, Jason Merrill wrote:
>>> @@ -8213,13 +8281,14 @@ modifying_const_object_p (tree_code code
>>>      return false;
>>>    }
>>> -/* Evaluate an INIT_EXPR or MODIFY_EXPR.  */
>>> +/* Evaluate an INIT_EXPR or MODIFY_EXPR.  START_LIFETIME is true
>>> +   when evaluating __builtin_start_lifetime.  */
>>>    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*/)
>>
>> Hmm, I don't love adding a parameter for this case, but I guess a clobber
>> won't work because this is a nop for an object that's already in lifetime.
> 
> The init in that case is not a clobber, but an empty CONSTRUCTOR with
> the right flags.  Making start_lifetime implied from the
> CONSTRUCTOR_OMITTED_NOT_WITHIN_LIFETIME_P bit wouldn't be reliable.
> What we could do (but not done in the patch) is use some tree other than
> MODIFY_EXPR/INIT_EXPR with 2+ operands instead of the current MODIFY_EXPR,
> say MODOP_EXPR and let that mean start_lifetime.

I like the sound of that, but won't insist on it.

>>> +constexpr A fA () { A a; a.a[0] = 1; return a; }
>>> +constexpr A a = fA ();		// { dg-error "is not a constant expression" "" { xfail *-*-* } }
>>
>> I don't see any discussion of this xfail above (or a comment here).  Is
>> there a PR?
> 
> I've filed PR127235 for this.  It is a preexisting bug (since C++20
> P1331R2 implementation I guess).  Dunno if here it is an error already
> to return the only partially initialized aggregate or only to initialize
> a constexpr var to that.  There is
>    /* The result of a constexpr function must be completely initialized.
> 
>       However, in C++20, a constexpr constructor doesn't necessarily have
>       to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
>       in order to detect reading an uninitialized object in constexpr instead
>       of value-initializing it.  (reduced_constant_expression_p is expected to
>       take care of clearing the flag.)  */
>    if (TREE_CODE (result) == CONSTRUCTOR
>        && (cxx_dialect < cxx20
>            || !DECL_CONSTRUCTOR_P (fun)))
>      clear_no_implicit_zero (result);
> and fun here is not a constructor, so we happily clear the
> CONSTRUCTOR_NO_CLEARING bits.  Either it is invalid to return something like
> that, in that case we should be actually doing VERIFY_CONSTANT somewhere
> where we currently don't do it, or it is not and then we shouldn't ever call
> clear_no_implicit_zero for C++ >= 20.

It's non-constant under https://eel.is/c++draft/expr.const#core-2.12 
which was also added by P1331; it's fine for the 'a' variable to be 
partially initialized, but not to copy that uninitialized value.  The 
problem is that by copying the struct as a whole we bypass the uninit 
check in cxx_eval_component_reference.

I guess _store_expression should reject init with CONSTRUCTOR_NO_CLEARING?

> Now, the standard wording isn't clear on what happens when the
> function template is called on an array where its containing subobject
> isn't in lifetime either; the patch acts for the surrounding subobjects
> as a store, so makes all subobjects except for union members and the
> object it is called on within lifetime.  If something else should happen
> (e.g. make it non-constant), the patch will need changing.

That sounds fine to me.

Jason



More information about the Libstdc++ mailing list