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