https://gcc.gnu.org/g:cce6cc2a791b5617716568a7ae33ef5f71e313cd

commit r17-602-gcce6cc2a791b5617716568a7ae33ef5f71e313cd
Author: Marek Polacek <[email protected]>
Date:   Fri May 15 14:51:29 2026 -0400

    c++: another constexpr nested empty object [PR125336]
    
    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.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 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(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 701269cd3f60..6c6d2f3fa994 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.  */
@@ -6883,7 +6887,7 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
        /* This is an initializer for an empty field; now that we've
           checked that it's constant, we can ignore it.  */
        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 000000000000..11099698a9bb
--- /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" }
+
+struct S{ };
+constexpr S& f (S&);
+union W {
+  [[no_unique_address]] S v;
+};
+struct R { W w; };
+S s;
+auto x = R{{ f(s) }};
diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C 
b/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C
new file mode 100644
index 000000000000..bea8a65b8a0d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C
@@ -0,0 +1,12 @@
+// PR c++/125336
+// { dg-do compile { target c++20 } }
+// { dg-prune-output "used but never defined" }
+
+struct S { int i; };
+constexpr S& f (S&);
+union W {
+  [[no_unique_address]] S v;
+};
+struct R { W w; };
+S s;
+auto x = R{{ f(s) }};

Reply via email to