Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

C++20 made a class with only explicitly defaulted constructors no longer
aggregate, and this wrongly affected whether the class is considered "POD
for layout purposes" under the ABI.

Conveniently, we already have check_non_pod_aggregate to diagnose cases
where this makes a difference, due to PR103681 around a C++14 aggregate
change.

        PR c++/120012

gcc/cp/ChangeLog:

        * cp-tree.h (struct lang_type): Add non_aggregate_pod.
        (CLASSTYPE_NON_AGGREGATE_POD): New.
        * class.cc (check_bases_and_members): Set it.
        (check_non_pod_aggregate): Diagnose it.

gcc/ChangeLog:

        * doc/invoke.texi: Document C++20 aggregate fix.
        * common.opt: Likewise.

gcc/testsuite/ChangeLog:

        * g++.dg/abi/base-defaulted1.C: New test.
        * g++.dg/abi/base-defaulted1a.C: New test.
---
 gcc/doc/invoke.texi                         |  3 +-
 gcc/common.opt                              |  1 +
 gcc/cp/cp-tree.h                            |  8 ++-
 gcc/cp/class.cc                             | 54 ++++++++++++++++-----
 gcc/testsuite/g++.dg/abi/base-defaulted1.C  | 19 ++++++++
 gcc/testsuite/g++.dg/abi/base-defaulted1a.C | 23 +++++++++
 6 files changed, 95 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/base-defaulted1.C
 create mode 100644 gcc/testsuite/g++.dg/abi/base-defaulted1a.C

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e7a9a03bace..32bc45725de 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3015,7 +3015,8 @@ 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).
+in noexcept lambdas (c++/119764) and layout of a base class
+with all explicitly defaulted constructors (c++/120012).
 
 See also @option{-Wabi}.
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 8a5b69d0767..0e50305dde8 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1056,6 +1056,7 @@ Driver Undocumented
 ;     Default in G++ 15.
 ;
 ; 21: Fix noexcept lambda capture pruning.
+;     Fix C++20 layout of base with all explicitly defaulted constructors.
 ;     Default in G++ 16.
 ;
 ; Additional positive integers will be assigned as new versions of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 856202c65dd..af51d67ef9f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2491,6 +2491,7 @@ struct GTY(()) lang_type {
   unsigned unique_obj_representations_set : 1;
   bool erroneous : 1;
   bool non_pod_aggregate : 1;
+  bool non_aggregate_pod : 1;
 
   /* When adding a flag here, consider whether or not it ought to
      apply to a template instance if it applies to the template.  If
@@ -2499,7 +2500,7 @@ struct GTY(()) lang_type {
   /* There are some bits left to fill out a 32-bit word.  Keep track
      of this by updating the size of this bitfield whenever you add or
      remove a flag.  */
-  unsigned dummy : 3;
+  unsigned dummy : 2;
 
   tree primary_base;
   vec<tree_pair_s, va_gc> *vcall_indices;
@@ -2826,6 +2827,11 @@ struct GTY(()) lang_type {
    with a hash_set only filled in when abi_version_crosses (17).  */
 #define CLASSTYPE_NON_POD_AGGREGATE(NODE) \
   (LANG_TYPE_CLASS_CHECK (NODE)->non_pod_aggregate)
+
+/* True if this class is layout-POD though it's not an aggregate in C++20 and
+   above (c++/120012).  This could also be a hash_set.  */
+#define CLASSTYPE_NON_AGGREGATE_POD(NODE) \
+  (LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate_pod)
 
 /* Additional macros for inheritance information.  */
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 2b694b98e56..6767ac10358 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -6413,9 +6413,7 @@ check_bases_and_members (tree t)
      Again, other conditions for being an aggregate are checked
      elsewhere.  */
   CLASSTYPE_NON_AGGREGATE (t)
-    |= ((cxx_dialect < cxx20
-        ? type_has_user_provided_or_explicit_constructor (t)
-        : TYPE_HAS_USER_CONSTRUCTOR (t))
+    |= (type_has_user_provided_or_explicit_constructor (t)
        || TYPE_POLYMORPHIC_P (t));
   /* This is the C++98/03 definition of POD; it changed in C++0x, but we
      retain the old definition internally for ABI reasons.  */
@@ -6437,6 +6435,20 @@ check_bases_and_members (tree t)
        CLASSTYPE_NON_LAYOUT_POD_P (t) = true;
     }
 
+  /* P1008: Prohibit aggregates with user-declared constructors.  */
+  if (cxx_dialect >= cxx20 && TYPE_HAS_USER_CONSTRUCTOR (t))
+    {
+      CLASSTYPE_NON_AGGREGATE (t) = true;
+      if (!CLASSTYPE_NON_LAYOUT_POD_P (t))
+       {
+         /* c++/120012: The C++20 aggregate change affected layout.  */
+         if (!abi_version_at_least (21))
+           CLASSTYPE_NON_LAYOUT_POD_P (t) = true;
+         if (abi_version_crosses (21))
+           CLASSTYPE_NON_AGGREGATE_POD (t) = true;
+       }
+    }
+
   /* If the only explicitly declared default constructor is user-provided,
      set TYPE_HAS_COMPLEX_DFLT.  */
   if (!TYPE_HAS_COMPLEX_DFLT (t)
@@ -6809,7 +6821,8 @@ end_of_class (tree t, eoc_mode mode)
 static void
 check_non_pod_aggregate (tree field)
 {
-  if (!abi_version_crosses (17) || cxx_dialect < cxx14)
+  if ((!abi_version_crosses (17) || cxx_dialect < cxx14)
+      && (!abi_version_crosses (21) || cxx_dialect < cxx20))
     return;
   if (TREE_CODE (field) != FIELD_DECL
       || (!DECL_FIELD_IS_BASE (field)
@@ -6822,7 +6835,8 @@ check_non_pod_aggregate (tree field)
   tree type = TREE_TYPE (field);
   if (TYPE_IDENTIFIER (type) == as_base_identifier)
     type = TYPE_CONTEXT (type);
-  if (!CLASS_TYPE_P (type) || !CLASSTYPE_NON_POD_AGGREGATE (type))
+  if (!CLASS_TYPE_P (type) || (!CLASSTYPE_NON_POD_AGGREGATE (type)
+                              && !CLASSTYPE_NON_AGGREGATE_POD (type)))
     return;
   tree size = end_of_class (type, (DECL_FIELD_IS_BASE (field)
                                   ? eoc_nvsize : eoc_nv_or_dsize));
@@ -6831,13 +6845,31 @@ check_non_pod_aggregate (tree field)
     {
       location_t loc = DECL_SOURCE_LOCATION (next);
       if (DECL_FIELD_IS_BASE (next))
-       warning_at (loc, OPT_Wabi,"offset of %qT base class for "
-                   "%<-std=c++14%> and up changes in "
-                   "%<-fabi-version=17%> (GCC 12)", TREE_TYPE (next));
+       {
+         if (abi_version_crosses (17)
+             && CLASSTYPE_NON_POD_AGGREGATE (type))
+           warning_at (loc, OPT_Wabi,"offset of %qT base class for "
+                       "%<-std=c++14%> and up changes in "
+                       "%<-fabi-version=17%> (GCC 12)", TREE_TYPE (next));
+         else if (abi_version_crosses (21)
+                  && CLASSTYPE_NON_AGGREGATE_POD (type))
+           warning_at (loc, OPT_Wabi,"offset of %qT base class for "
+                       "%<-std=c++20%> and up changes in "
+                       "%<-fabi-version=21%> (GCC 16)", TREE_TYPE (next));
+       }
       else
-       warning_at (loc, OPT_Wabi, "offset of %qD for "
-                   "%<-std=c++14%> and up changes in "
-                   "%<-fabi-version=17%> (GCC 12)", next);
+       {
+         if (abi_version_crosses (17)
+             && CLASSTYPE_NON_POD_AGGREGATE (type))
+           warning_at (loc, OPT_Wabi, "offset of %qD for "
+                       "%<-std=c++14%> and up changes in "
+                       "%<-fabi-version=17%> (GCC 12)", next);
+         else if (abi_version_crosses (21)
+                  && CLASSTYPE_NON_AGGREGATE_POD (type))
+           warning_at (loc, OPT_Wabi, "offset of %qD for "
+                       "%<-std=c++20%> and up changes in "
+                       "%<-fabi-version=21%> (GCC 16)", next);
+       }
     }
 }
 
diff --git a/gcc/testsuite/g++.dg/abi/base-defaulted1.C 
b/gcc/testsuite/g++.dg/abi/base-defaulted1.C
new file mode 100644
index 00000000000..aaada72528f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/base-defaulted1.C
@@ -0,0 +1,19 @@
+// PR c++/120012
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fabi-version=21 -Wabi=20" }
+
+struct A
+{
+   A(const A&) = default;
+   A(A&&) = default;
+   A& operator=(A&&) = default;
+   unsigned int a;
+   unsigned char b;
+};
+struct B: A
+{
+   unsigned char c;            // { dg-warning "offset" "" { target c++20 } }
+};
+
+static_assert(sizeof(A) == (2 * sizeof(unsigned int)), "");
+static_assert(sizeof(B) == (3 * sizeof(unsigned int)), "");
diff --git a/gcc/testsuite/g++.dg/abi/base-defaulted1a.C 
b/gcc/testsuite/g++.dg/abi/base-defaulted1a.C
new file mode 100644
index 00000000000..d61eb39bb9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/base-defaulted1a.C
@@ -0,0 +1,23 @@
+// PR c++/120012
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fabi-version=20 -Wabi" }
+
+struct A
+{
+   A(const A&) = default;
+   A(A&&) = default;
+   A& operator=(A&&) = default;
+   unsigned int a;
+   unsigned char b;
+};
+struct B: A
+{
+   unsigned char c;            // { dg-warning "offset" "" { target c++20 } }
+};
+
+static_assert(sizeof(A) == (2 * sizeof(unsigned int)), "");
+#if __cplusplus >= 202002L
+static_assert(sizeof(B) == (2 * sizeof(unsigned int)), "");
+#else
+static_assert(sizeof(B) == (3 * sizeof(unsigned int)), "");
+#endif

base-commit: 4af5de21363cfdd2be227c05dfdee7e053337f6a
-- 
2.49.0

Reply via email to