On Thu, Oct 08, 2026 at 01:16:15PM +0200, Jakub Jelinek wrote:
> 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?

Here is incremental change to just ignore everything what is inside of
unions, assuming copying of those never results in lvalue-to-rvalue
conversion and so even copying of indeterminate values is ok.

The libstdc++ inplace_vector FAILs are gone with this as expected,
a strange thing is that this isn't what clang++ implements either.
Looking at constexpr-init24.C, they do diagnose foo (11), foo (14),
foo (20), foo (23), foo (29), while this incremental patch does not.
In the delta between the previous patch and clang++ it is
foo (18), foo (19), foo (22), foo (28), corge (2), corge (7) and
corge (8).
11/14/20/23/29 are about a struct with union member with active struct member
which has some members indeterminate.
18/19/22/28 are just a union with active struct member which has
some members indeterminate, ditto corge.

--- a/gcc/cp/constexpr.cc       2026-10-08 13:36:19.822284304 +0200
+++ b/gcc/cp/constexpr.cc       2026-10-08 13:36:40.982004201 +0200
@@ -3382,16 +3382,8 @@ find_uninit_subobject (tree t, tree sz,
       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;
-    }
+    /* 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)));
--- a/gcc/testsuite/g++.dg/cpp26/trivial-union6.C       2026-10-07 
17:49:38.504057082 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/trivial-union6.C       2026-10-08 
13:37:59.440965602 +0200
@@ -12,27 +12,27 @@ union G { int a; A b[2]; };
 
 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 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 B
 fB2 ()
 {
   B a;
   __builtin_start_lifetime (&a.b);
   a.b[0] = 1;
-  return a;                    // { dg-error "accessing uninitialized 
subobject 'B::b\\\[1\\\]'" }
+  return a;
 }
-constexpr B b2 = fB2 ();
+constexpr B b2 = fB2 ();       // { dg-error "is not a constant expression" "" 
{ target c++23_down } }
 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;                    // { dg-error "accessing uninitialized 
subobject 'C::b\\\[0\\\]\\\[1\\\]'" }
+  return a;
 }
-constexpr C c2 = fC2 (false);
-constexpr C c3 = fC2 (true);
+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 D
 fD2 ()
 {
   D a;
   __builtin_start_lifetime (&a.a.b);
   a.a.b[0] = 1;
-  return a;                    // { dg-error "accessing uninitialized 
subobject 'D::a.B::b\\\[1\\\]'" }
+  return a;
 }
-constexpr D d2 = fD2 ();
+constexpr D d2 = fD2 ();       // { dg-error "is not a constant expression" "" 
{ target c++23_down } }
 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;                    // { dg-error "accessing uninitialized 
subobject 'E::a.C::b\\\[0\\\]\\\[1\\\]'" }
+  return a;
 }
-constexpr E e2 = fE2 (false);
-constexpr E e3 = fE2 (true);
+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
 fE3 (bool x)
 {
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-init24.C     2026-10-07 
13:31:38.442354373 +0200
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init24.C     2026-10-08 
13:40:11.166221884 +0200
@@ -55,7 +55,7 @@ foo (int x)
     e = d;
   d.j.h.a = 8;
   if (x == 11)
-    e = d;                     // { dg-error "accessing uninitialized 
subobject 'D::j.C::h.A::b'" }
+    e = d;
   d.j.h.b = 9;
   if (x == 12)
     e = d;
@@ -64,7 +64,7 @@ foo (int x)
     e = d;
   d.j.h.b = 10;
   if (x == 14)
-    e = d;                     // { dg-error "accessing uninitialized 
subobject 'D::j.C::h.A::a'" }
+    e = d;
   C f, g;
   f = {};
   if (x == 15)
@@ -77,17 +77,17 @@ foo (int x)
     g = f;
   f.h.a = {};
   if (x == 18)
-    g = f;                     // { dg-error "accessing uninitialized 
subobject 'C::h.A::b'" }
+    g = f;
   if (x == 19)
-    C h = f;                   // { dg-error "accessing uninitialized 
subobject 'C::h.A::b'" }
+    C h = f;
   else if (x == 20)
-    D i = d;                   // { dg-error "accessing uninitialized 
subobject 'D::j.C::h.A::a'" }
+    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);                   // { dg-error "accessing uninitialized 
subobject 'C::h.A::b'" }
+    baz (f);
   else if (x == 23)
-    baz (d);                   // { dg-error "accessing uninitialized 
subobject 'D::j.C::h.A::a'" }
+    baz (d);
   else if (x == 24)
     baz (b);                   // { dg-error "accessing uninitialized 
subobject 'B::d.A::b'" }
   else if (x == 25)
@@ -97,9 +97,9 @@ foo (int x)
   else if (x == 27)
     baz <B &> (b);
   else if (x == 28)
-    qux (true, f);             // { dg-error "accessing uninitialized 
subobject 'C::h.A::b'" }
+    qux (true, f);
   else if (x == 29)
-    qux (true, d);             // { dg-error "accessing uninitialized 
subobject 'D::j.C::h.A::a'" }
+    qux (true, d);
   else if (x == 30)
     qux (true, b);             // { dg-error "accessing uninitialized 
subobject 'B::d.A::b'" }
   return true;
@@ -150,7 +150,7 @@ corge (int x)
     {
       C b;
       b.h.a = 1;
-      return b;                        // { dg-error "accessing uninitialized 
subobject 'C::h.A::b'" }
+      return b;
     }
   else if (x == 3)
     {
@@ -169,8 +169,8 @@ corge (int x)
     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 }
+  return d;
+}
 
 constexpr bool a = foo (1);
 constexpr bool b = foo (2);


        Jakub

Reply via email to