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