On Fri, Oct 09, 2026 at 05:52:15PM +0200, Tomasz Kaminski wrote: > Implementations can, of course make these copies because they are not > observable > for trivially copyable types by the program, other than cases like this. We > need to pretend that they do not happen.
Ok, here is an updated patch. Compared to the earlier posted one it doesn't for now diagnose anything inside of the unions, the reflector discussion hasn't resulted in a CWG with a proposed resolution yet, it can be easily changed when there is a proposed resolution, doesn't try to diagnose anything in cxx_bind_parameters_in_call for TREE_ADDRESSABLE argument types, diagnoses it also during TARGET_EXPR evaluation for temporaries including the function argument ones and drops the clear_no_implicit_zero calls for C++20 and later (this one could be restored when also the union uninit subobjects are diagnosed, though it is probably undesirable anyway). The testcase now includes all the proposed copy elision tests, both with a !TREE_ADDRESSABLE and with a TREE_ADDRESSABLE class. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-10-13 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. Don't call clear_no_implicit_zero for C++20 or later. (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: Remove xfailed dg-error, add dg-error with different diagnostics on a line before that. * 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. * g++.dg/cpp1y/constexpr-nsdmi6b.C: Expect extra diagnostics for C++20. --- a/gcc/cp/cp-tree.h 2026-10-09 13:35:33.979484740 +0200 +++ b/gcc/cp/cp-tree.h 2026-10-09 19:00:53.727086862 +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-09 13:35:33.973484820 +0200 +++ b/gcc/cp/constexpr.cc 2026-10-12 14:25:15.898986169 +0200 @@ -3291,6 +3291,215 @@ 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) + /* Union copying is done by copying object representation. */ + 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. ORIG_INIT is the original + tree of INIT before constant evaluation. */ + +static bool +check_uninit_subobjects (const constexpr_ctx *ctx, + location_t init_loc, tree init, tree orig_init) +{ + /* If the copy is guaranteed to be elided by the standard, + don't diagnose anything. For calls returning aggregates + by values or hidden reference we assume diagnostics of the + copying of uninitialized subobjects is done on the callee + side. */ + if (orig_init) + { + if (TREE_CODE (orig_init) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (orig_init, 0)) == TARGET_EXPR + && (TREE_OPERAND (orig_init, 1) + == TARGET_EXPR_SLOT (TREE_OPERAND (orig_init, 0)))) + orig_init = TREE_OPERAND (orig_init, 0); + if (TREE_CODE (orig_init) == TARGET_EXPR + && TARGET_EXPR_INITIAL (orig_init) + && (TARGET_EXPR_ELIDING_P (orig_init) + || !TREE_ADDRESSABLE (TREE_TYPE (orig_init)))) + orig_init = TARGET_EXPR_INITIAL (orig_init); + while (TREE_CODE (orig_init) == COMPOUND_EXPR) + orig_init = TREE_OPERAND (orig_init, 1); + if (TREE_CODE (orig_init) == AGGR_INIT_EXPR + || TREE_CODE (orig_init) == VEC_INIT_EXPR + || (TREE_CODE (orig_init) == CALL_EXPR + && AGGREGATE_TYPE_P (TREE_TYPE (orig_init)))) + return false; + } + + 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 +3615,19 @@ cxx_bind_parameters_in_call (const const /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p && ctx->quiet) break; + + if (cxx_dialect >= cxx20 + /* For TREE_ADDRESSABLE types this is checked on TARGET_EXPR + evaluation instead. */ + && !TREE_ADDRESSABLE (type) + && !*non_constant_p + && TREE_CODE (arg) == CONSTRUCTOR) + { + location_t arg_loc = cp_expr_loc_or_input_loc (x); + if (check_uninit_subobjects (ctx, arg_loc, arg, x)) + *non_constant_p = true; + } + /* Just discard ellipsis args after checking their constantitude. */ if (!parms) continue; @@ -5041,6 +5263,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); @@ -5215,9 +5438,7 @@ cxx_eval_call_expression (const constexp 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))) + if (TREE_CODE (result) == CONSTRUCTOR && cxx_dialect < cxx20) clear_no_implicit_zero (result); pop_cx_call_context (); @@ -8985,6 +9206,27 @@ 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, + TREE_CODE (t) == INIT_EXPR + ? TREE_OPERAND (t, 1) : NULL_TREE)) + { + *non_constant_p = true; + return t; + } + } + /* Don't share a CONSTRUCTOR that might be changed later. */ init = unshare_constructor (init); @@ -10084,6 +10326,16 @@ 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, + DECL_INITIAL (r))) + *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 @@ -10170,6 +10422,22 @@ cxx_eval_constant_expression (const cons ctx->global->put_value (slot, NULL_TREE); return NULL_TREE; } + if (cxx_dialect >= cxx20 + && TREE_CODE (r) == CONSTRUCTOR + && !VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (t)))) + { + location_t init_loc + = cp_expr_loc_or_input_loc (TARGET_EXPR_INITIAL (t)); + if (check_uninit_subobjects (ctx, init_loc, r, + TARGET_EXPR_INITIAL (t))) + { + *non_constant_p = true; + if (!is_complex + && !(AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))) + ctx->global->put_value (slot, NULL_TREE); + return NULL_TREE; + } + } if (!is_complex) { r = unshare_constructor (r); --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-92443.C 2026-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-92443.C 2026-10-09 19:00:53.729877180 +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-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union6.C 2026-10-09 19:00:53.730047810 +0200 @@ -10,8 +10,8 @@ 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 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; } 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; } --- a/gcc/testsuite/g++.dg/cpp26/trivial-union7.C 2026-10-09 19:00:53.730158989 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union7.C 2026-10-09 19:00:53.730158989 +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-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-10-09 19:00:53.730333149 +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-09 19:00:53.730588056 +0200 +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init24.C 2026-10-12 14:36:21.490878278 +0200 @@ -0,0 +1,274 @@ +// 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; }; +struct E { constexpr E (int p) : x (p) {}; int x, y; }; +struct F { constexpr F (int p) : x (p) {}; int x, y; constexpr ~F () {} }; + +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; + 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; + 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; + if (x == 19) + C h = f; + else if (x == 20) + D i = d; + else if (x == 21) + B j = b; // { dg-error "accessing uninitialized subobject 'B::d.A::b'" } + else if (x == 22) + baz (f); + else if (x == 23) + baz (d); + 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); + else if (x == 29) + qux (true, d); + 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; + } + 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; +} + +template <typename T> +constexpr T +waldo () +{ + return T (10); +} + +template <typename T> +constexpr void +xyzzy (T) +{ +} + +constexpr bool +boo (int z) +{ + E a = E (10); // OK, no copy + xyzzy (E (10)); // OK, no copy + E b = waldo <E> (); // OK, no copy + xyzzy (waldo <E> ()); // OK, no copy + F d = F (10); // OK, no copy + xyzzy (F (10)); // OK, no copy + F e = waldo <F> (); // OK, no copy + xyzzy (waldo <F> ()); // OK, no copy + + if (z == 1) + E c = a; // { dg-error "accessing uninitialized subobject 'E::y'" } + else if (z == 2) + xyzzy (a); // { dg-error "accessing uninitialized subobject 'E::y'" } + else if (z == 3) + F f = d; // { dg-error "accessing uninitialized subobject 'F::y'" } + else if (z == 4) + xyzzy (d); // { dg-error "accessing uninitialized subobject 'F::y'" } + return true; +} + +void +grault () +{ + constexpr E a = waldo <E> (); // { dg-error "is not a constant expression" } + constexpr E b (10); // { dg-error "is not a constant expression" } + constexpr F c = waldo <F> (); // { dg-error "is not a constant expression" } + constexpr F d (10); // { dg-error "is not a constant expression" } +} + +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)); +static_assert (foo (11)); +static_assert (foo (12)); +static_assert (foo (13)); +static_assert (foo (14)); +static_assert (foo (15)); +static_assert (foo (16)); +static_assert (foo (17)); +static_assert (foo (18)); +static_assert (foo (19)); +static_assert (foo (20)); +constexpr bool l = foo (21); +static_assert (foo (22)); +static_assert (foo (23)); +constexpr bool o = foo (24); +static_assert (foo (25)); +static_assert (foo (26)); +static_assert (foo (27)); +static_assert (foo (28)); +static_assert (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); +static_assert (boo (0)); +constexpr bool z = boo (1); +constexpr bool aa = boo (2); +constexpr bool ab = boo (3); +constexpr bool ac = boo (4); +constexpr E ad = waldo <E> (); // { dg-error "is not a constant expression" } +constexpr E ae (10); // { dg-error "is not a constant expression" } +constexpr F af = waldo <F> (); // { dg-error "is not a constant expression" } +constexpr F ag (10); // { dg-error "is not a constant expression" } --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C 2026-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C 2026-10-09 19:00:53.730761513 +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-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C 2026-10-09 19:00:53.730908575 +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-10-09 13:35:33.980484727 +0200 +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C 2026-10-09 19:00:53.731058959 +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-10-09 13:35:33.982528504 +0200 +++ b/gcc/testsuite/g++.dg/reflect/bases_of5.C 2026-10-09 19:00:53.731220637 +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); } --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi6b.C 2026-03-27 10:17:15.572307274 +0100 +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi6b.C 2026-10-13 08:48:09.531863858 +0200 @@ -21,7 +21,7 @@ baz() { constexpr A a = foo(); // { dg-error ".A..& a... is not a constant expression" } constexpr A b = bar(); // { dg-error ".A..& b... is not a constant expression" } -} - +} // { dg-error "'foo\\\(\\\)' is not a constant expression because it refers to an incompletely initialized variable" "" { target *-*-* } .-2 } + // { dg-error "'bar\\\(\\\)' is not a constant expression because it refers to an incompletely initialized variable" "" { target *-*-* } .-2 } constexpr A a = foo(); constexpr A b = bar(); Jakub
