On 5/18/26 12:56 PM, Marek Polacek wrote:
On Fri, May 15, 2026 at 06:05:29PM -0400, Patrick Palka wrote:
On Fri, 15 May 2026, Marek Polacek wrote:

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/branches?

-- >8 --
When looking into 125315 I came up with another test that crashes
due to an empty object.  It still crashes even after Jason's patch.

Here we have a subobject nested in an empty object:

   {.w = {.v = TARGET_EXPR <f(s)>}}

w's type is W, an empty union due to [[no_unique_address]], so
init_subob_ctx clears the ctor, but then we recurse to

   {.v = TARGET_EXPR <f(s)>}

with a null ctx->ctor so the call to get_or_insert_ctor_field in
cxx_eval_bare_aggregate crashes.  This fixes the crash similarly
to c++/125315.

no_unique_address18.C worked fine even before this patch because
there we don't have an empty object.  But let's test it also.

        PR c++/125336

gcc/cp/ChangeLog:

        * constexpr.cc (cxx_eval_bare_aggregate): Don't call
        get_or_insert_ctor_field when there is no CONSTRUCTOR.  Assert
        is_empty_class.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/no_unique_address17.C: New test.
        * g++.dg/cpp2a/no_unique_address18.C: New test.
---
  gcc/cp/constexpr.cc                              |  8 ++++++--
  gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C | 12 ++++++++++++
  gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C | 12 ++++++++++++
  3 files changed, 30 insertions(+), 2 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 1fc8a56503c..84896170d05 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -6841,7 +6841,11 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree 
t,
        /* Like in cxx_eval_store_expression, omit entries for empty fields.  */
        bool no_slot = new_ctx.ctor == NULL_TREE;
        int pos_hint = -1;
-      if (new_ctx.ctor != ctx->ctor && !no_slot)
+      if (!ctx->ctor)
+       /* The enclosing object could be an empty subobject so we have no
+          CONSTRUCTOR (c++/125336).  */
+       gcc_checking_assert (is_empty_class (type));
+      else if (new_ctx.ctor != ctx->ctor && !no_slot)
        {
          /* If we built a new CONSTRUCTOR, attach it now so that other
             initializers can refer to it.  */
@@ -6892,7 +6896,7 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
          gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (index))));
          changed = true;
        }
-      else
+      else if (ctx->ctor)
        {
          if (TREE_CODE (type) == UNION_TYPE
              && (*p)->last().index != index)
diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C 
b/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C
new file mode 100644
index 00000000000..11099698a9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C
@@ -0,0 +1,12 @@
+// PR c++/125336
+// { dg-do compile { target c++20 } }
+// { dg-prune-output "used but never defined" }

The "used but never defined" warning implies the testcase is UB right?
Would be nice to have a valid testcase that triggers this bug.

It would be very nice but I don't know how to construct such
a test.  If the class didn't have to be empty, I would know
how.  To exhibit the problem we need to call cxx_eval_bare_aggregate
which is not going to happen when the CONSTRUCTOR is constant.

Right, because we exit early on

    case CONSTRUCTOR:
      if (TREE_CONSTANT (t) && reduced_constant_expression_p (t))
        {
          /* Don't re-process a constant CONSTRUCTOR.  */
          verify_constructor_flags (t);
          if (TREE_CONSTANT (t))
            return t;
        }

so we only run into problems if it wasn't already marked as constant, which is difficult to reproduce.

Incidentally, in this case we have a CONSTRUCTOR that does have entries for the empty classes. Even if we change W to be a struct rather than a union, there's an entry for R::w. Which, it occurs to me, seems fine because R::w is not an "empty field".

But that's a bit beside the point.  The patch is OK.

Jason

Reply via email to