On Thu, Oct 8, 2026 at 1:16 PM Jakub Jelinek <[email protected]> wrote:
> Hi! > > C++20 P1331R2 has added http://eel.is/c++draft/expr.const#core-2.12 > - an lvalue-to-rvalue conversion that is applied to an object with an > indeterminate value; > as one of the cases which result in an expression not being core constant > expression. > Now, if we are reading a scalar with indeterminate value, we already > usually diagnose it, but when copying an aggregate with uninitialized > subobjects, we can accept it even when it should be rejected, whether > the copying is done because of trivial copy/move construction or trivial > copy/move assignment. > > The following patch attempts to diagnose it / make evaluation > *non_constant_p in such cases, in 3 places (cxx_eval_store_expression > for copying of the aggregates including returning them by value, > cxx_bind_parameters_in_call for passing aggregates by value and > cxx_eval_constant_expression DECL_EXPR handling for constructing > objects by copying aggregate). > There are some problems I've run into: > 1) the current behavior in the patch on constexpr-init24.C differs > in some spots from how clang++ behaves. > Small testcase: > struct S { int a, b; }; > union U { int c; S d; }; > constexpr U foo () { U u; u.d.a = 42; return u; } > static_assert (foo ().d.a == 42); > Now, Tomasz pointed out that https://eel.is/c++draft/class.copy.ctor#16 > and https://eel.is/c++draft/class.copy.assign#13 define copying/moving > of unions by copy of object representation, so maybe in the current > standard wording the above is valid. I think copying of object > representation is the only reasonable thing for runtime (together with > saying that it starts lifetime of the subobjects that were within > lifetime on the other side), but I believe it is a wrong thing to do > for constant evaluation. The object representation and lifetime > is not the only thing we care about during constant evaluation, an > important case is e.g. for pointers the knowledge whether they point > to the start of a complete object or past the end of a different > complete object which happens to have the same object representation, > or e.g. whether the value is indeterminate or not. > Shall I keep it as is in the patch and discuss on CWG reflector, > or shall I change the patch to basically ignore anything inside of > unions for this? > 2) when trying to implement the bit_cast related part of P1272R4, I've > assumed that the changes mentioned in that paper are meant for > constant evaluation as well; but if returning or otherwise copying > a struct with indeterminate subobjects is not valid constant expression, > a lot of what those testcases test is now just invalid > 3) in an earlier version of the patch, I had basically all of reflection > tests FAIL, because cxx_eval_call_expression uses > /* If this is a constructor, we are beginning the lifetime of the > object we are initializing. */ > hunk to activate a union member and implements that with an > cxx_eval_constant_expression on a made up INIT_EXPR with > CONSTRUCTOR_NO_CLEARING empty ctor on the rhs; that triggered > the new diagnostics/*non_constant_p. I've added a flag on INIT_EXPR > with an ugly name to work around this > 4) on trivial-union6.C testcase, many spots now result in different > diagnostics because we diagnose returning structures with indeterminate > subobjects; in order to partly preserve what the test was doing, I've > added trivial-union7.C test which instead of returning those has those > in default constructor and so no copying is involved > 5) there is one unresolved FAIL in g++.dg - g++.dg/cpp2a/constexpr-dtor17.C > for C++20 and above; the errors are > cpp2a/constexpr-dtor17.C:38:17: error: non-constant condition for > static assertion > cpp2a/constexpr-dtor17.C:17:40: error: accessing uninitialized > subobject 'basic_string::_M_local_buf' > cpp2a/constexpr-dtor17.C:39:17: error: non-constant condition for > static assertion > cpp2a/constexpr-dtor17.C:17:40: error: accessing uninitialized > subobject 'basic_string::_M_local_buf' > Now the test is maybe reduced too much, but even adding constexpr user > defined copy ctor and copy assignment op doesn't help. > The get() method uses INIT_EXPR from a TARGET_EXPR for the return, > in check_return_expr > /* Actually copy the value returned into the appropriate location. */ > if (retval && retval != result) > retval = cp_build_init_expr (result, retval); > > if (current_function_return_value == bare_retval) > INIT_EXPR_NRV_P (retval) = true; > where INIT_EXPR_NRV_P isn't set in this case. If we shouldn't > diagnose it in this case, how to differentiate that from the > case where one simply returns an aggregate by value? > 6) there are some libstdc++ FAILs caused by this patch: > FAIL: 23_containers/inplace_vector/move.cc -std=gnu++26 (test for > excess errors) > FAIL: 23_containers/inplace_vector/move.cc -std=gnu++29 (test for > excess errors) > FAIL: 23_containers/inplace_vector/copy.cc -std=gnu++26 (test for > excess errors) > FAIL: 23_containers/inplace_vector/copy.cc -std=gnu++29 (test for > excess errors) > FAIL: 23_containers/inplace_vector/modifiers/multi_insert.cc > -std=gnu++26 (test for excess errors) > FAIL: 23_containers/inplace_vector/modifiers/multi_insert.cc > -std=gnu++29 (test for excess errors) > FAIL: std/format/constexpr.cc -std=gnu++26 (test for excess errors) > FAIL: std/format/constexpr.cc -std=gnu++29 (test for excess errors) > FAIL: std/format/functions/format.cc -std=gnu++20 (test for excess > errors) > e.g. > error: accessing uninitialized subobject 'std::inplace_vector<int, > 5>::<anonymous>.std::inplace_vector<int, 5>::<unnamed union>::_M_elems[0]' > Now, I admit it isn't clear if P3726R2 is somehow supposed to interact > with this, maybe if 1) is changed to ignore unions then at least > the inplace_vector ones would go away. Or if we change what the > trivial copying of unions does during constant evaluation, whether > the lvalue-to-rvalue conversion doesn't happen for inactive union > elemental subobjects. > An object must be within its lifetime to have a value, so it cannot be indeterminate or uninitialized. I have allowed such out-of lifetime members only in unions because the standard never copies union elements (the bit about copying object representation). If we want to change to a constexpr path, to copy active union elements, we do lvalue-to-rvalue conversion only of active union member objects. > The format errors are like: > format:2552: error: accessing uninitialized subobject > 'std::__format::_Spec<char>::_M_width' > I will try to take a look on this one, if they are no gone with next patch. > > 2026-10-08 Jakub Jelinek <[email protected]> > > PR c++/127235 > * cp-tree.h (INIT_EXPR_UNION_LIFETIME_START_P): Define. > * constexpr.cc (any_scalar_subobject, find_uninit_subobject, > check_uninit_subobjects): New functions. > (cxx_bind_parameters_in_call): Use check_uninit_subobjects. > (cxx_eval_call_expression): Mark INIT_EXPR with > INIT_EXPR_UNION_LIFETIME_START_P flag. > (cxx_eval_store_expression, cxx_eval_constant_expression): Use > check_uninit_subobjects. > > * g++.dg/cpp0x/constexpr-92443.C: Expect diagnostics for C++20. > * g++.dg/cpp26/trivial-union6.C: Expect different diagnostics. > * g++.dg/cpp26/trivial-union7.C: New test. > * g++.dg/cpp26/within-lifetime1.C (std::bit_cast): Remove. > (foo): Remove tests with std::bit_cast. > * g++.dg/cpp2a/constexpr-init24.C: New test. > * g++.dg/cpp2a/bit-cast11.C: Expect different diagnostics for > C++20. > * g++.dg/cpp2a/bit-cast12.C: Likewise. > * g++.dg/cpp2a/bit-cast14.C: Likewise. > * g++.dg/reflect/bases_of5.C (_Vector_impl): Initialize _M_start > to nullptr in the ctor. > > --- a/gcc/cp/cp-tree.h 2026-09-25 17:52:15.181532762 +0200 > +++ b/gcc/cp/cp-tree.h 2026-10-07 15:47:31.888619582 +0200 > @@ -509,6 +509,7 @@ extern GTY(()) tree cp_global_trees[CPTI > MUST_NOT_THROW_CATCH_P (in MUST_NOT_THROW_EXPR) > MULTIPLE_NAMES_PARM_P (in PARM_DECL) > SPLICE_EXPR_ADDRESS_P (in SPLICE_EXPR) > + INIT_EXPR_UNION_LIFETIME_START_P (in INIT_EXPR) > 3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR) > ICS_BAD_FLAG (in _CONV) > FN_TRY_BLOCK_P (in TRY_BLOCK) > @@ -4517,6 +4518,12 @@ struct GTY(()) lang_decl { > #define INIT_EXPR_NRV_P(NODE) \ > TREE_LANG_FLAG_0 (INIT_EXPR_CHECK (NODE)) > > +/* True iff this represents starting of lifetime of a union member. > + In such case we shouldn't check for indeterminate subobjects on the > + rhs. */ > +#define INIT_EXPR_UNION_LIFETIME_START_P(NODE) \ > + TREE_LANG_FLAG_2 (INIT_EXPR_CHECK (NODE)) > + > #define CALL_OR_AGGR_INIT_CHECK(NODE) \ > TREE_CHECK2 ((NODE), CALL_EXPR, AGGR_INIT_EXPR) > > --- a/gcc/cp/constexpr.cc 2026-10-07 10:13:02.740411711 +0200 > +++ b/gcc/cp/constexpr.cc 2026-10-07 17:58:04.370425876 +0200 > @@ -3291,6 +3291,196 @@ addr_of_non_const_var (tree *tp, int *wa > return NULL_TREE; > } > > +/* Helper function of find_uninit_subobject. Return true if TYPE contains > + any scalar subobject except for subobjects of unions. This is called > on > + types of subobjects of CONSTRUCTOR_NO_PADDING elements which are > missing > + in the CONSTRUCTOR. For union it means no union member is considered > + active. */ > +static bool > +any_scalar_subobject (tree type) > +{ > + type = strip_array_types (type); > + if (type == error_mark_node || TREE_CODE (type) == UNION_TYPE) > + return false; > + if (TREE_CODE (type) == RECORD_TYPE) > + { > + for (tree field = next_subobject_field (TYPE_FIELDS (type)); > + field; field = next_subobject_field (DECL_CHAIN (field))) > + if (any_scalar_subobject (TREE_TYPE (field))) > + return true; > + return false; > + } > + return true; > +} > + > +/* Return true if T contains an uninitialized member and leave in > + COMPONENTS a vector of the indices. > + If called recursively on a FIELD_DECL's CONSTRUCTOR, SZ > + is DECL_SIZE of the FIELD_DECL, otherwise NULL. */ > + > +static bool > +find_uninit_subobject (tree t, tree sz, vec<tree> *components) > +{ > + if (t == void_node) > + return true; > + if (TREE_CODE (t) != CONSTRUCTOR) > + return false; > + if (!AGGREGATE_TYPE_P (TREE_TYPE (t))) > + return true; > + if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) > + { > + tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t))); > + tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t))); > + tree cursor = CONSTRUCTOR_NO_CLEARING (t) ? min : NULL_TREE; > + int any_scalar_subobject_p = -1; > + for (auto &e: CONSTRUCTOR_ELTS (t)) > + { > + if (cursor && array_index_cmp (cursor, e.index)) > + { > + if (any_scalar_subobject_p == -1) > + any_scalar_subobject_p > + = any_scalar_subobject (TREE_TYPE (TREE_TYPE (t))); > + if (any_scalar_subobject_p) > + { > + components->safe_push (cursor); > + return true; > + } > + else > + cursor = e.index; > + } > + components->safe_push (e.index); > + if (find_uninit_subobject (e.value, NULL_TREE, components)) > + return true; > + components->pop (); > + if (!cursor) > + continue; > + if (TREE_CODE (e.index) == RANGE_EXPR) > + cursor = TREE_OPERAND (e.index, 1); > + if (TREE_CODE (e.value) == RAW_DATA_CST) > + cursor > + = int_const_binop (PLUS_EXPR, cursor, > + size_int (RAW_DATA_LENGTH (e.value))); > + else > + cursor = int_const_binop (PLUS_EXPR, cursor, > + size_one_node); > + } > + if (cursor) > + { > + max = int_const_binop (PLUS_EXPR, max, size_one_node); > + if (array_index_cmp (cursor, max)) > + { > + if (any_scalar_subobject_p == -1) > + any_scalar_subobject_p > + = any_scalar_subobject (TREE_TYPE (TREE_TYPE (t))); > + if (any_scalar_subobject_p) > + { > + components->safe_push (cursor); > + return true; > + } > + } > + } > + return false; > + } > + if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE) > + { > + if (CONSTRUCTOR_NELTS (t) == 0) > + return false; > + components->safe_push (CONSTRUCTOR_ELT (t, 0)->index); > + if (find_uninit_subobject (CONSTRUCTOR_ELT (t, 0)->value, > + NULL_TREE, components)) > + return true; > + components->pop (); > + return false; > + } > + tree field = NULL_TREE; > + if (CONSTRUCTOR_NO_CLEARING (t)) > + field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t))); > + for (auto &e: CONSTRUCTOR_ELTS (t)) > + { > + for (; field && e.index != field; > + field = next_subobject_field (DECL_CHAIN (field))) > + if (any_scalar_subobject (TREE_TYPE (field))) > + { > + components->safe_push (field); > + return true; > + } > + components->safe_push (e.index); > + if (find_uninit_subobject (e.value, DECL_SIZE (e.index), > + components)) > + return true; > + components->pop (); > + if (field) > + field = next_subobject_field (DECL_CHAIN (field)); > + } > + for (; field; field = next_subobject_field (DECL_CHAIN (field))) > + if (any_scalar_subobject (TREE_TYPE (field))) > + { > + /* Ignore FIELD_DECLs with bit positions beyond DECL_SIZE of > + the parent FIELD_DECL (if any) for classes with virtual bases. > */ > + if (cxx_dialect >= cxx26 > + && sz > + && tree_int_cst_le (sz, bit_position (field))) > + break; > + components->safe_push (field); > + return true; > + } > + return false; > +} > + > +/* Return true if INIT contains any uninitialized subobjects and > + if !CTX->quiet, diagnose it as well. */ > + > +static bool > +check_uninit_subobjects (const constexpr_ctx *ctx, > + location_t init_loc, tree init) > +{ > + auto_vec <tree, 8> components; > + if (!find_uninit_subobject (init, NULL_TREE, &components)) > + return false; > + if (!ctx->quiet) > + { > + if (AGGREGATE_TYPE_P (TREE_TYPE (init))) > + { > + pretty_printer pp; > + pretty_printer *refpp = global_dc->get_reference_printer (); > + pp_format_decoder (&pp) = pp_format_decoder (refpp); > + pp.set_format_postprocessor > + (pp_format_postprocessor (refpp)->clone ()); > +#if __GNUC__ >= 10 > +#pragma GCC diagnostic push > +#pragma GCC diagnostic ignored "-Wformat" > +#pragma GCC diagnostic ignored "-Wformat-diag" > +#endif > + unsigned int i; > + tree c; > + FOR_EACH_VEC_ELT (components, i, c) > + { > + if (TREE_CODE (c) == INTEGER_CST) > + { > + pp_character (&pp, '['); > + pp_printf (&pp, "%E", c); > + pp_character (&pp, ']'); > + } > + else > + { > + if (i) > + pp_character (&pp, '.'); > + pp_printf (&pp, "%D", c); > + } > + } > +#if __GNUC__ >= 10 > +#pragma GCC diagnostic pop > +#endif > + error_at (init_loc, "accessing uninitialized subobject %qs", > + pp_formatted_text (&pp)); > + } > + else > + error_at (init_loc, "reading indeterminate value of type %qT", > + TREE_TYPE (init)); > + } > + return true; > +} > + > /* Subroutine of cxx_eval_call_expression. > We are processing a call expression (either CALL_EXPR or > AGGR_INIT_EXPR) in the context of CTX. Evaluate > @@ -3406,6 +3596,16 @@ cxx_bind_parameters_in_call (const const > /* Don't VERIFY_CONSTANT here. */ > if (*non_constant_p && ctx->quiet) > break; > + > + if (cxx_dialect >= cxx20 > + && TREE_CODE (arg) == CONSTRUCTOR > + && !*non_constant_p) > + { > + location_t arg_loc = cp_expr_loc_or_input_loc (x); > + if (check_uninit_subobjects (ctx, arg_loc, arg)) > + *non_constant_p = true; > + } > + > /* Just discard ellipsis args after checking their constantitude. > */ > if (!parms) > continue; > @@ -5041,6 +5241,7 @@ cxx_eval_call_expression (const constexp > CONSTRUCTOR_NO_CLEARING (ctor) = true; > tree activate = build2 (INIT_EXPR, TREE_TYPE (new_obj), > new_obj, ctor); > + INIT_EXPR_UNION_LIFETIME_START_P (activate) = true; > cxx_eval_constant_expression (ctx, activate, > lval, non_constant_p, > overflow_p, > jump_target); > @@ -8985,6 +9186,25 @@ cxx_eval_store_expression (const constex > if (*non_constant_p) > return t; > > + /* For C++20 and above check if aggregate assignment or default > construction > + doesn't copy uninitialized subobjects, because that violates > + [expr.const]/(2.12): an lvalue-to-rvalue conversion that is applied > to > + an object with an indeterminate value. */ > + if (cxx_dialect >= cxx20 > + && TREE_CODE (init) == CONSTRUCTOR > + && !TREE_CLOBBER_P (init) > + && TREE_CODE (t) != MODOP_EXPR > + && (TREE_CODE (t) != INIT_EXPR > + || !INIT_EXPR_UNION_LIFETIME_START_P (t))) > + { > + location_t init_loc = cp_expr_loc_or_input_loc (TREE_OPERAND (t, > 1)); > + if (check_uninit_subobjects (ctx, init_loc, init)) > + { > + *non_constant_p = true; > + return t; > + } > + } > + > /* Don't share a CONSTRUCTOR that might be changed later. */ > init = unshare_constructor (init); > > @@ -10084,6 +10304,15 @@ cxx_eval_constant_expression (const cons > jump_target); > if (*jump_target) > return NULL_TREE; > + if (cxx_dialect >= cxx20 > + && TREE_CODE (init) == CONSTRUCTOR > + && !*non_constant_p) > + { > + location_t init_loc > + = cp_expr_loc_or_input_loc (DECL_INITIAL (r)); > + if (check_uninit_subobjects (ctx, init_loc, init)) > + *non_constant_p = true; > + } > /* Don't share a CONSTRUCTOR that might be changed. */ > init = unshare_constructor (init); > /* Remember that a constant object's constructor has already > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-92443.C 2026-03-27 > 10:17:15.418309787 +0100 > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-92443.C 2026-10-07 > 17:24:54.562533390 +0200 > @@ -9,4 +9,4 @@ struct a { > long b; > }; > using d = a; > -constexpr d e = 6, f = f | e; > +constexpr d e = 6, f = f | e; // { dg-error "accessing uninitialized > subobject 'a::b'" "" { target c++20 } } > --- a/gcc/testsuite/g++.dg/cpp26/trivial-union6.C 2026-09-10 > 16:56:41.692638816 +0200 > +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union6.C 2026-10-07 > 17:49:38.504057082 +0200 > @@ -10,29 +10,29 @@ 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" } > -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" } > -constexpr D fD () { D a; a.a.b[0] = 1; return a; } > -constexpr D d = fD (); // { dg-error "is not a constant > expression" } > -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" } > -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" } > +constexpr A fA () { A a; a.a[0] = 1; return a; } // { dg-error > "accessing uninitialized subobject 'A::a\\\[1\\\]'" } > +constexpr A a = fA (); > +constexpr B fB () { B a; a.b[0] = 1; return a; } // { dg-error > "accessing uninitialized subobject 'B::b\\\[1\\\]'" } > +constexpr B b = fB (); > +constexpr C fC () { C a; a.b[0][0] = 1; a.b[1][0] = 2; a.b[1][1] = 3; > return a; } // { dg-error "accessing uninitialized subobject > 'C::b\\\[0\\\]\\\[1\\\]'" } > +constexpr C c = fC (); > +constexpr D fD () { D a; a.a.b[0] = 1; return a; } // { dg-error > "accessing uninitialized subobject 'D::a.B::b\\\[1\\\]'" } > +constexpr D d = fD (); > +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; } // { dg-error "accessing uninitialized subobject > 'E::a.C::b\\\[0\\\]\\\[1\\\]'" } > +constexpr E e = fE (); > +constexpr F fF () { F a; a.b.a[0] = 1; return a; } // { dg-error > "accessing uninitialized subobject 'F::b.A::a\\\[1\\\]'" } > +constexpr F f = fF (); > +constexpr G fG () { G a; a.b[0].a[0] = 1; return a; } // { dg-error > "accessing uninitialized subobject 'G::b\\\[0\\\].A::a\\\[1\\\]'" } > +constexpr G g = fG (); > constexpr B > fB2 () > { > B a; > __builtin_start_lifetime (&a.b); > a.b[0] = 1; > - return a; > + return a; // { dg-error "accessing uninitialized > subobject 'B::b\\\[1\\\]'" } > } > -constexpr B b2 = fB2 (); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr B b2 = fB2 (); > constexpr C > fC2 (bool x) > { > @@ -44,19 +44,19 @@ fC2 (bool x) > a.b[0][0] = 1; > a.b[1][0] = 2; > a.b[1][1] = 3; > - return a; > + return a; // { dg-error "accessing uninitialized > subobject 'C::b\\\[0\\\]\\\[1\\\]'" } > } > -constexpr C c2 = fC2 (false); // { dg-error "is not a constant > expression" "" { target c++23_down } } > -constexpr C c3 = fC2 (true); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr C c2 = fC2 (false); > +constexpr C c3 = fC2 (true); > constexpr D > fD2 () > { > D a; > __builtin_start_lifetime (&a.a.b); > a.a.b[0] = 1; > - return a; > + return a; // { dg-error "accessing uninitialized > subobject 'D::a.B::b\\\[1\\\]'" } > } > -constexpr D d2 = fD2 (); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr D d2 = fD2 (); > constexpr E > fE2 (bool x) > { > @@ -68,10 +68,10 @@ fE2 (bool x) > a.a.b[0][0] = 1; > a.a.b[1][0] = 2; > a.a.b[1][1] = 3; > - return a; > + return a; // { dg-error "accessing uninitialized > subobject 'E::a.C::b\\\[0\\\]\\\[1\\\]'" } > } > -constexpr E e2 = fE2 (false); // { dg-error "is not a constant > expression" "" { target c++23_down } } > -constexpr E e3 = fE2 (true); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr E e2 = fE2 (false); > +constexpr E e3 = fE2 (true); > constexpr E > fE3 (bool x) > { > --- a/gcc/testsuite/g++.dg/cpp26/trivial-union7.C 2026-10-07 > 19:10:03.791539734 +0200 > +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union7.C 2026-10-07 > 19:35:19.844409324 +0200 > @@ -0,0 +1,61 @@ > +// 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]; }; > + > +struct H { constexpr H () { a.a[0] = 1; } A a; }; > +constexpr H h; // { dg-error "is not a constant expression" } > +struct I { constexpr I () { a.b[0] = 1; } B a; }; > +constexpr I i; // { dg-error "is not a constant expression" } > +struct J { constexpr J () { a.b[0][0] = 1; a.b[1][0] = 2; a.b[1][1] = 3; > } C a; }; > +constexpr J j; // { dg-error "is not a constant expression" } > +struct K { constexpr K () { a.a.b[0] = 1; } D a; }; > +constexpr K k; // { dg-error "is not a constant expression" } > +struct L { constexpr L () { a.a.b[0][0] = 1; a.a.b[1][0] = 2; a.a.b[1][1] > = 3; } E a; }; > +constexpr L l; // { dg-error "is not a constant expression" } > +struct M { constexpr M () { a.b.a[0] = 1; } F a; }; > +constexpr M m; // { dg-error "is not a constant expression" } > +struct N { constexpr N () { a.b[0].a[0] = 1; } G a; }; > +constexpr N n; // { dg-error "is not a constant expression" } > +struct O { constexpr O () { __builtin_start_lifetime (&a.b); a.b[0] = 1; > } B a; }; > +constexpr O o; // { dg-error "is not a constant expression" "" { target > c++23_down } } > +struct P { constexpr P (bool x) { > + __builtin_start_lifetime (&a.b); > + __builtin_start_lifetime (&a.b[0]); > + if (x) > + __builtin_start_lifetime (&a.b[1]); > + a.b[0][0] = 1; > + a.b[1][0] = 2; > + a.b[1][1] = 3; > +} C a; }; > +constexpr P pf (false); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr P pt (true); // { dg-error "is not a constant expression" "" { > target c++23_down } } > +struct Q { constexpr Q () { __builtin_start_lifetime (&a.a.b); a.a.b[0] = > 1; } D a; }; > +constexpr Q q; // { dg-error "is not a constant expression" "" { target > c++23_down } } > +struct R { constexpr R (bool x) { > + __builtin_start_lifetime (&a.a.b); > + __builtin_start_lifetime (&a.a.b[0]); > + if (x) > + __builtin_start_lifetime (&a.a.b[1]); > + a.a.b[0][0] = 1; > + a.a.b[1][0] = 2; > + a.a.b[1][1] = 3; > +} E a; }; > +constexpr R rf (false); // { dg-error "is not a constant > expression" "" { target c++23_down } } > +constexpr R rt (true); // { dg-error "is not a constant expression" "" { > target c++23_down } } > +struct S { constexpr S (bool x) { > + __builtin_start_lifetime (&a.a.b[0]); // { dg-error > "'__builtin_start_lifetime' with containing object not within lifetime" } > + if (x) > + __builtin_start_lifetime (&a.a.b[1]); > + a.a.b[0][0] = 1; > + a.a.b[1][1] = 3; > +} E a; }; > +constexpr S sf (false); > +constexpr S st (true); > --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-09-14 > 18:13:08.000000000 +0200 > +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-10-07 > 17:52:42.254648368 +0200 > @@ -12,15 +12,6 @@ namespace std { > } > #endif > > -namespace std { > - template <typename T, typename F> > - constexpr T > - bit_cast (const F &f) noexcept > - { > - return __builtin_bit_cast (T, f); > - } > -} > - > constexpr char d = 0; > struct E { char a; union F { int b; long c; short d; struct G { int e; } > f[2]; } g; }; > constexpr E e = {}; > @@ -149,15 +140,9 @@ foo () > return __LINE__; > if (!std::is_within_lifetime (&k)) > return __LINE__; > - unsigned char l = std::bit_cast <unsigned char> (k); > - if (!std::is_within_lifetime (&l)) > - return __LINE__; > struct {} m; > if (!std::is_within_lifetime (&m)) > return __LINE__; > - unsigned char n = std::bit_cast <unsigned char> (m); > - if (!std::is_within_lifetime (&n)) > - return __LINE__; > int o; > if (!std::is_within_lifetime (static_cast <volatile int *> (&o))) > return __LINE__; > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-init24.C 2026-10-07 > 10:40:43.626150885 +0200 > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init24.C 2026-10-07 > 13:31:38.442354373 +0200 > @@ -0,0 +1,218 @@ > +// PR c++/127235 > +// { dg-do compile { target c++20 } } > + > +struct A { int a, b; }; > +struct B { int c; A d; int e; }; > +union C { int f; long g; A h; }; > +struct D { A i[3]; C j; }; > + > +template <typename T> > +constexpr bool > +baz (T x) > +{ > + return true; > +} > + > +constexpr bool > +qux (bool x, ...) > +{ > + return x; > +} > + > +constexpr bool > +foo (int x) > +{ > + B b, c; > + b.c = 1; > + b.d.a = 2; > + b.e = 3; > + D d, e; > + d.i[0] = {}; > + d.i[1] = {}; > + d.i[2].b = 4; > + if (x == 1) > + c.d.b = b.d.b; // { dg-error "accessing uninitialized > member 'A::b'" } > + else if (x == 2) > + c.d = b.d; // { dg-error "accessing uninitialized > subobject 'A::b'" } > + else if (x == 3) > + c = b; // { dg-error "accessing uninitialized > subobject 'B::d.A::b'" } > + else if (x == 4) > + e.i[2].a = d.i[2].a; // { dg-error "accessing uninitialized > member 'A::a'" } > + else if (x == 5) > + e.i[2] = d.i[2]; // { dg-error "accessing uninitialized > subobject 'A::a'" } > + else if (x == 6) > + e.i[1] = d.i[1]; > + else if (x == 7) > + e = d; // { dg-error "accessing uninitialized > subobject 'D::i\\\[2\\\].A::a'" } > + d.i[2].a = 5; > + if (x == 8) > + e = d; > + d.j.f = 6; > + if (x == 9) > + e = d; > + d.j.g = 7; > + if (x == 10) > + e = d; > + d.j.h.a = 8; > + if (x == 11) > + e = d; // { dg-error "accessing uninitialized > subobject 'D::j.C::h.A::b'" } > + d.j.h.b = 9; > + if (x == 12) > + e = d; > + d = {}; > + if (x == 13) > + e = d; > + d.j.h.b = 10; > + if (x == 14) > + e = d; // { dg-error "accessing uninitialized > subobject 'D::j.C::h.A::a'" } > + C f, g; > + f = {}; > + if (x == 15) > + g = f; > + f.h = {}; > + if (x == 16) > + g = f; > + f.g = {}; > + if (x == 17) > + g = f; > + f.h.a = {}; > + if (x == 18) > + g = f; // { dg-error "accessing uninitialized > subobject 'C::h.A::b'" } > + if (x == 19) > + C h = f; // { dg-error "accessing uninitialized > subobject 'C::h.A::b'" } > + else if (x == 20) > + D i = d; // { dg-error "accessing uninitialized > subobject 'D::j.C::h.A::a'" } > + else if (x == 21) > + B j = b; // { dg-error "accessing uninitialized > subobject 'B::d.A::b'" } > + else if (x == 22) > + baz (f); // { dg-error "accessing uninitialized > subobject 'C::h.A::b'" } > + else if (x == 23) > + baz (d); // { dg-error "accessing uninitialized > subobject 'D::j.C::h.A::a'" } > + else if (x == 24) > + baz (b); // { dg-error "accessing uninitialized > subobject 'B::d.A::b'" } > + else if (x == 25) > + baz <C &> (f); > + else if (x == 26) > + baz <D &> (d); > + else if (x == 27) > + baz <B &> (b); > + else if (x == 28) > + qux (true, f); // { dg-error "accessing uninitialized > subobject 'C::h.A::b'" } > + else if (x == 29) > + qux (true, d); // { dg-error "accessing uninitialized > subobject 'D::j.C::h.A::a'" } > + else if (x == 30) > + qux (true, b); // { dg-error "accessing uninitialized > subobject 'B::d.A::b'" } > + return true; > +} > + > +constexpr A > +bar (int x) > +{ > + A a; > + if (x == 1 || x == 3) > + a.a = 1; > + if (x == 2 || x == 3) > + a.b = 2; > + return a; // { dg-error "accessing uninitialized > subobject 'A::a'" } > +} // { dg-error "accessing uninitialized > subobject 'A::b'" "" { target *-*-* } .-1 } > + > +constexpr B > +fred (int x) > +{ > + if (x == 1) > + { > + B a; > + a.c = 1; > + a.d.a = 2; > + a.e = 3; > + return a; // { dg-error "accessing > uninitialized subobject 'B::d.A::b'" } > + } > + else if (x == 2) > + { > + B b; > + b.c = 4; > + b.d.b = 5; > + b.e = 6; > + return b; // { dg-error "accessing > uninitialized subobject 'B::d.A::a'" } > + } > + return {}; > +} > + > +constexpr C > +corge (int x) > +{ > + if (x == 1) > + { > + C a; > + return a; > + } > + else if (x == 2) > + { > + C b; > + b.h.a = 1; > + return b; // { dg-error "accessing > uninitialized subobject 'C::h.A::b'" } > + } > + else if (x == 3) > + { > + C c; > + c.g = 42; > + return c; > + } > + C d; > + if (x == 5) > + d.f = 1; > + else if (x == 6) > + d.g = 2; > + else if (x == 7) > + d.h.b = 3; > + else if (x == 8) > + d.h.a = 4; > + else if (x == 9) > + d.h.a = d.h.b = 5; > + return d; // { dg-error "accessing uninitialized > subobject 'C::h.A::a'" } > +} // { dg-error "accessing uninitialized > subobject 'C::h.A::b'" "" { target *-*-* } .-1 } > + > +constexpr bool a = foo (1); > +constexpr bool b = foo (2); > +constexpr bool c = foo (3); > +constexpr bool d = foo (4); > +constexpr bool e = foo (5); > +static_assert (foo (6)); > +constexpr bool f = foo (7); > +static_assert (foo (8)); > +static_assert (foo (9)); > +static_assert (foo (10)); > +constexpr bool g = foo (11); > +static_assert (foo (12)); > +static_assert (foo (13)); > +constexpr bool h = foo (14); > +static_assert (foo (15)); > +static_assert (foo (16)); > +static_assert (foo (17)); > +constexpr bool i = foo (18); > +constexpr bool j = foo (19); > +constexpr bool k = foo (20); > +constexpr bool l = foo (21); > +constexpr bool m = foo (22); > +constexpr bool n = foo (23); > +constexpr bool o = foo (24); > +static_assert (foo (25)); > +static_assert (foo (26)); > +static_assert (foo (27)); > +constexpr bool p = foo (28); > +constexpr bool q = foo (29); > +constexpr bool r = foo (30); > +constexpr int s = bar (1).a; > +constexpr int t = bar (2).b; > +static_assert (bar (3).a == 1); > +constexpr int u = fred (1).d.a; > +constexpr int v = fred (2).d.b; > +static_assert ((corge (1), true)); > +constexpr int w = corge (2).h.a; > +static_assert (corge (3).g == 42); > +static_assert ((corge (4), true)); > +static_assert (corge (5).f == 1); > +static_assert (corge (6).g == 2); > +constexpr int x = corge (7).h.b; > +constexpr int y = corge (8).h.a; > +static_assert (corge (9).h.a == 5); > --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C 2026-03-27 > 10:17:15.660305838 +0100 > +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C 2026-10-08 > 10:26:30.691073100 +0200 > @@ -11,7 +11,7 @@ constexpr bool > f1 () > { > T t = { 1, 2 }; > - S s = __builtin_bit_cast (S, t); > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing uninitialized > subobject 'S::a\\\[1\\\]'" "" { target c++20 } } > return s.a[0] == 1; > } > > @@ -19,7 +19,7 @@ constexpr bool > f2 () > { > U u = { 0, 0 }; > - unsigned char a = __builtin_bit_cast (unsigned char, u); > + unsigned char a = __builtin_bit_cast (unsigned char, u); // { dg-error > "reading indeterminate value of type 'unsigned char'" "" { target c++20 } } > return true; > } > > @@ -27,23 +27,23 @@ constexpr bool > f3 () > { > T t = { 1, 2 }; > - S s = __builtin_bit_cast (S, t); > - return s.a[1] == 0; // { dg-error "accessing uninitialized > array element" } > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing uninitialized > subobject 'S::a\\\[1\\\]'" "" { target c++20 } } > + return s.a[1] == 0; // { dg-error "accessing uninitialized > array element" "" { target c++17_down } } > } > > constexpr bool > f4 () > { > U u = { 0, 0 }; > - unsigned char a = __builtin_bit_cast (unsigned char, u); > - return a == 0; // { dg-error "is not a constant > expression" } > + unsigned char a = __builtin_bit_cast (unsigned char, u); // { dg-error > "reading indeterminate value of type 'unsigned char'" "" { target c++20 } } > + return a == 0; // { dg-error "is not a constant > expression" "" { target c++17_down } } > } > > constexpr bool > f5 () > { > W t = { 1, 2 }; > - V s = __builtin_bit_cast (V, t); > + V s = __builtin_bit_cast (V, t); // { dg-error "accessing uninitialized > subobject 'V::b.S::a\\\[1\\\]'" "" { target c++20 } } > return s.b.a[0] == 1; > } > > @@ -51,13 +51,13 @@ constexpr bool > f6 () > { > W t = { 1, 2 }; > - V s = __builtin_bit_cast (V, t); > - return s.b.a[1] == 1; // { dg-error "accessing > uninitialized array element" } > + V s = __builtin_bit_cast (V, t); // { dg-error "accessing uninitialized > subobject 'V::b.S::a\\\[1\\\]'" "" { target c++20 } } > + return s.b.a[1] == 1; // { dg-error "accessing > uninitialized array element" "" { target c++17_down } } > } > > -constexpr bool a = f1 (); > -constexpr bool b = f2 (); > +constexpr bool a = f1 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > +constexpr bool b = f2 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > constexpr bool c = f3 (); // { dg-message "in .constexpr. expansion" > } > constexpr bool d = f4 (); // { dg-message "in .constexpr. expansion" > } > -constexpr bool e = f5 (); > +constexpr bool e = f5 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > constexpr bool f = f6 (); // { dg-message "in .constexpr. expansion" > } > --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C 2026-03-27 > 10:17:15.660305838 +0100 > +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C 2026-10-08 > 10:36:25.502885551 +0200 > @@ -6,7 +6,7 @@ namespace std > enum class byte : unsigned char {}; > } > > -struct S { unsigned char a[2]; alignas(sizeof 0) int b; }; > +struct S { std::byte a[2]; alignas(sizeof 0) int b; }; > struct T { char a; alignas(sizeof 0) int b; }; > struct U { char a : 1; char : 6; char b : 1; }; > struct V { int a; S b; }; > @@ -16,15 +16,15 @@ constexpr bool > f1 () > { > T t = { 1, 2 }; > - S s = __builtin_bit_cast (S, t); > - return s.a[0] == 1; > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing uninitialized > subobject 'S::a\\\[1\\\]'" "" { target c++20 } } > + return s.a[0] == std::byte (1); > } > > constexpr bool > f2 () > { > U u = { 0, 0 }; > - unsigned char a = __builtin_bit_cast (unsigned char, u); > + std::byte a = __builtin_bit_cast (std::byte, u); // { dg-error "reading > indeterminate value of type 'std::byte'" "" { target c++20 } } > return true; > } > > @@ -32,37 +32,37 @@ constexpr bool > f3 () > { > T t = { 1, 2 }; > - S s = __builtin_bit_cast (S, t); > - return s.a[1] == 0; // { dg-error "accessing uninitialized > array element" } > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing uninitialized > subobject 'S::a\\\[1\\\]'" "" { target c++20 } } > + return s.a[1] == std::byte (0); // { dg-error "accessing uninitialized > array element" "" { target c++17_down } } > } > > constexpr bool > f4 () > { > U u = { 0, 0 }; > - unsigned char a = __builtin_bit_cast (unsigned char, u); > - return a == 0; // { dg-error "is not a constant > expression" } > + std::byte a = __builtin_bit_cast (std::byte, u); // { dg-error "reading > indeterminate value of type 'std::byte'" "" { target c++20 } } > + return a == std::byte (0); // { dg-error "is not a constant > expression" "" { target c++17_down } } > } > > constexpr bool > f5 () > { > W t = { 1, 2 }; > - V s = __builtin_bit_cast (V, t); > - return s.b.a[0] == 1; > + V s = __builtin_bit_cast (V, t); // { dg-error "accessing uninitialized > subobject 'V::b.S::a\\\[1\\\]'" "" { target c++20 } } > + return s.b.a[0] == std::byte (1); > } > > constexpr bool > f6 () > { > W t = { 1, 2 }; > - V s = __builtin_bit_cast (V, t); > - return s.b.a[1] == 1; // { dg-error "accessing > uninitialized array element" } > + V s = __builtin_bit_cast (V, t); // { dg-error "accessing uninitialized > subobject 'V::b.S::a\\\[1\\\]'" "" { target c++20 } } > + return s.b.a[1] == std::byte (1); // { dg-error "accessing > uninitialized array element" "" { target c++17_down } } > } > > -constexpr bool a = f1 (); > -constexpr bool b = f2 (); > +constexpr bool a = f1 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > +constexpr bool b = f2 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > constexpr bool c = f3 (); // { dg-message "in .constexpr. expansion" > } > constexpr bool d = f4 (); // { dg-message "in .constexpr. expansion" > } > -constexpr bool e = f5 (); > +constexpr bool e = f5 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > constexpr bool f = f6 (); // { dg-message "in .constexpr. expansion" > } > --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C 2026-03-27 > 10:17:15.660305838 +0100 > +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C 2026-10-08 > 10:48:52.696607070 +0200 > @@ -11,7 +11,7 @@ constexpr bool > f1 () > { > T1 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::a'" "" { target c++20 } } > return true; > } > > @@ -19,7 +19,7 @@ constexpr bool > f2 () > { > T2 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::b'" "" { target c++20 } } > return true; > } > > @@ -27,7 +27,7 @@ constexpr bool > f3 () > { > T3 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::c'" "" { target c++20 } } > return true; > } > > @@ -43,8 +43,8 @@ constexpr bool > f5 () > { > T1 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > - unsigned char a = s.a; // { dg-error "accessing > uninitialized member" } > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::a'" "" { target c++20 } } > + unsigned char a = s.a; // { dg-error "accessing > uninitialized member" "" { target c++17_down } } > return true; > } > > @@ -52,8 +52,8 @@ constexpr bool > f6 () > { > T2 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > - unsigned char b = s.b; // { dg-error "accessing > uninitialized member" } > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::b'" "" { target c++20 } } > + unsigned char b = s.b; // { dg-error "accessing > uninitialized member" "" { target c++17_down } } > return true; > } > > @@ -61,14 +61,14 @@ constexpr bool > f7 () > { > T3 t = { 0, 0, 0, 0, 0, 0, 0 }; > - S s = __builtin_bit_cast (S, t); > - unsigned char c = s.c; // { dg-error "accessing > uninitialized member" } > + S s = __builtin_bit_cast (S, t); // { dg-error "accessing > uninitialized subobject 'S::c'" "" { target c++20 } } > + unsigned char c = s.c; // { dg-error "accessing > uninitialized member" "" { target c++17_down } } > return true; > } > > -constexpr bool a = f1 (); > -constexpr bool b = f2 (); > -constexpr bool c = f3 (); > +constexpr bool a = f1 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > +constexpr bool b = f2 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > +constexpr bool c = f3 (); // { dg-message "in .constexpr. expansion" > "" { target c++20 } } > constexpr bool d = f4 (); // { dg-message "in .constexpr. expansion" > } > constexpr bool e = f5 (); // { dg-message "in .constexpr. expansion" > } > constexpr bool f = f6 (); // { dg-message "in .constexpr. expansion" > } > --- a/gcc/testsuite/g++.dg/reflect/bases_of5.C 2026-05-14 > 09:51:59.000000000 +0200 > +++ b/gcc/testsuite/g++.dg/reflect/bases_of5.C 2026-10-08 > 11:18:57.465809669 +0200 > @@ -69,7 +69,7 @@ template <typename _Tp, typename _Alloc> > typedef __alloc_traits<_Alloc>::template rebind<_Tp>::other > _Tp_alloc_type; > typedef __alloc_traits<_Tp_alloc_type>::pointer pointer; > struct _Vector_impl : _Tp_alloc_type { > - constexpr _Vector_impl(_Tp_alloc_type) {} > + constexpr _Vector_impl(_Tp_alloc_type) : _M_start(nullptr) {} > pointer _M_start; > }; > constexpr ~_Vector_base() { _M_deallocate(_M_impl._M_start); } > > Jakub > >
