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

Reply via email to