Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk?

-- >8 --

Here the result of A::make(0, 0, 1), (0, 1, 0) and (1, 0, 0) are each
represented as a single-element CONSTRUCTOR with CONSTRUCTOR_NO_CLEARING
cleared, and we end up mangling them all as A{1}, i.e. eliding both the
implicit initial and trailing zeros.  Mangling them all the same seems
clearly wrong since they're logically different values.

This patch makes us include initial zeros in such mangling, which seems
like the natural behavior (and matches e.g. Clang).

        PR c++/121231

gcc/ChangeLog:

        * common.opt: Document additional -fabi-version=21 change.
        * doc/invoke.texi: Likewise.

gcc/cp/ChangeLog:

        * mangle.cc (write_expression):

gcc/testsuite/ChangeLog:

        * g++.dg/abi/mangle82.C: New test.
---
 gcc/common.opt                      |  2 +
 gcc/cp/mangle.cc                    | 49 ++++++++++++++++--
 gcc/doc/invoke.texi                 |  5 +-
 gcc/testsuite/g++.dg/abi/mangle82.C | 79 +++++++++++++++++++++++++++++
 4 files changed, 128 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/mangle82.C

diff --git a/gcc/common.opt b/gcc/common.opt
index 70659fabebd5..97b1e27456ce 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1067,6 +1067,8 @@ Driver Undocumented
 ;
 ; 21: Fix noexcept lambda capture pruning.
 ;     Fix C++20 layout of base with all explicitly defaulted constructors.
+;     Fix mangling of class and array objects with implicitly
+;     zero-initialized initial subojects.
 ;     Default in G++ 16.
 ;
 ; Additional positive integers will be assigned as new versions of
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 13d5dedebd29..f317dfd978ae 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -3739,11 +3739,50 @@ write_expression (tree expr)
              constructor_elt *ce;
 
              if (!undigested)
-               for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
-                 if ((TREE_CODE (etype) == UNION_TYPE
-                      && ce->index != first_field (etype))
-                     || !zero_init_expr_p (ce->value))
-                   last_nonzero = i;
+               {
+                 for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); 
++i)
+                   {
+                     if (i == 0
+                         && !CONSTRUCTOR_NO_CLEARING (expr))
+                       {
+                         /* Write out any implicit initial zeros.  */
+                         if (TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE)
+                           for (tree field = TYPE_FIELDS (TREE_TYPE (expr));
+                                field; field = DECL_CHAIN (field))
+                             {
+                               field = next_subobject_field (field);
+                               if (!field || field == ce->index)
+                                 break;
+                               if (abi_check (21))
+                                 write_expression (build_zero_cst
+                                                   (TREE_TYPE (field)));
+                             }
+                         else if (TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE)
+                           {
+                             unsigned HOST_WIDE_INT j;
+                             if (TREE_CODE (ce->index) == RANGE_EXPR)
+                               j = tree_to_uhwi (TREE_OPERAND (ce->index, 0));
+                             else
+                               j = tree_to_uhwi (ce->index);
+                             tree zero = NULL_TREE;
+                             for (; j != 0; --j)
+                               if (abi_check (21))
+                                 {
+                                   if (!zero)
+                                     zero = build_zero_cst (TREE_TYPE
+                                                            (TREE_TYPE 
(expr)));
+                                   write_expression (zero);
+                                 }
+                           }
+                       }
+
+                     if ((TREE_CODE (etype) == UNION_TYPE
+                          && ce->index != first_field (etype))
+                         || !zero_init_expr_p (ce->value))
+                       last_nonzero = i;
+                   }
+
+               }
 
              if (undigested || last_nonzero != UINT_MAX)
                for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e442a9cb73e4..23cd736e5fd1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3016,8 +3016,9 @@ Version 20, which first appeared in G++ 15, fixes 
manglings of lambdas
 in static data member initializers.
 
 Version 21, which first appeared in G++ 16, fixes unnecessary captures
-in noexcept lambdas (c++/119764) and layout of a base class
-with all explicitly defaulted constructors (c++/120012).
+in noexcept lambdas (c++/119764), layout of a base class with all explicitly
+defaulted constructors (c++/120012), and mangling of class and array
+objects with implicitly zero-initialized initial subojects (c++/121231).
 
 See also @option{-Wabi}.
 
diff --git a/gcc/testsuite/g++.dg/abi/mangle82.C 
b/gcc/testsuite/g++.dg/abi/mangle82.C
new file mode 100644
index 000000000000..529c75ee8997
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle82.C
@@ -0,0 +1,79 @@
+// Test mangling of C++20 class NTTP objects with implicitly zeroed initial 
subojects
+// PR c++/121231
+// { dg-do compile { target c++20 } }
+
+struct A {
+  int x, y, z;
+
+  static constexpr A make(int x, int y, int z) {
+    A a{};
+    if (x != 0)
+      a.x = x;
+    if (y != 0)
+      a.y = y;
+    if (z != 0)
+      a.z = z;
+    return a;
+  }
+
+};
+
+struct B : A {
+  int w;
+
+  static constexpr B make(int x, int y, int z, int w) {
+    B b{};
+    if (x != 0 || y != 0 || z != 0)
+      static_cast<A&>(b) = A::make(x, y, z);
+    if (w != 0)
+      b.w = w;
+    return b;
+  }
+};
+
+struct C {
+  int xyz[3];
+
+  static constexpr C make(int x, int y, int z) {
+    C c{};
+    if (x != 0)
+      c.xyz[0] = x;
+    if (y != 0)
+      c.xyz[1] = y;
+    if (z != 0)
+      c.xyz[2] = z;
+    return c;
+  }
+};
+
+template<int N, A a> void f();
+template<int N, B b> void g();
+template<int N, C c> void h();
+
+int main() {
+  f<0, A::make(0, 0, 1)>(); // void f<0, A{0, 0, 1}>()
+  f<1, A::make(0, 1, 0)>(); // void f<1, A{0, 1}>()
+  f<2, A::make(0, 0, 0)>(); // void f<2, A{}>()
+
+  g<0, B::make(0, 0, 0, 1)>(); // void g<0, B{A{}, 1}>()
+  g<1, B::make(0, 0, 1, 0)>(); // void g<1, B{A{0, 0, 1}}>()
+  g<2, B::make(0, 1, 0, 0)>(); // void g<2, B{A{0, 1}}>()
+  g<3, B::make(0, 0, 0, 0)>(); // void g<3, B{}>()
+
+  h<0, C::make(0, 0, 1)>(); // void h<0, C{int [3]{0, 0, 1}}>()
+  h<1, C::make(0, 1, 0)>(); // void h<1, C{int [3]{0, 1}}>()
+  h<2, C::make(0, 0, 0)>(); // void h<2, C{}>()
+}
+
+// { dg-final { scan-assembler "_Z1fILi0EXtl1ALi0ELi0ELi1EEEEvv" } }
+// { dg-final { scan-assembler "_Z1fILi1EXtl1ALi0ELi1EEEEvv" } }
+// { dg-final { scan-assembler "_Z1fILi2EXtl1AEEEvv" } }
+
+// { dg-final { scan-assembler "_Z1gILi0EXtl1Btl1AELi1EEEEvv" } }
+// { dg-final { scan-assembler "_Z1gILi1EXtl1Btl1ALi0ELi0ELi1EEEEEvv" } }
+// { dg-final { scan-assembler "_Z1gILi2EXtl1Btl1ALi0ELi1EEEEEvv" } }
+// { dg-final { scan-assembler "_Z1gILi3EXtl1BEEEvv" } }
+
+// { dg-final { scan-assembler "_Z1hILi0EXtl1CtlA3_iLi0ELi0ELi1EEEEEvv" } }
+// { dg-final { scan-assembler "_Z1hILi1EXtl1CtlA3_iLi0ELi1EEEEEvv" } }
+// { dg-final { scan-assembler "_Z1hILi2EXtl1CEEEvv" } }
-- 
2.50.1.469.ge813a0200a

Reply via email to