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.
The format errors are like:
format:2552: error: accessing uninitialized subobject
'std::__format::_Spec<char>::_M_width'
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