On Fri, Nov 21, 2025 at 05:43:35PM +0530, Jason Merrill wrote: > On 11/21/25 3:17 AM, Nathaniel Shead wrote: > > On Thu, Nov 20, 2025 at 10:28:13PM +0530, Jason Merrill wrote: > > > On 11/20/25 4:02 PM, Nathaniel Shead wrote: > > > > On Wed, Nov 19, 2025 at 12:08:58PM +0530, Jason Merrill wrote: > > > > > On 10/26/25 7:33 AM, Nathaniel Shead wrote: > > > > > > OK for trunk? > > > > > > > > > > > > -- >8 -- > > > > > > > > > > > > This reorders some checks in layout_compatible_type_p to promote > > > > > > more > > > > > > useful diagnostics as well and try to avoid duplicate code. > > > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > > > * constraint.cc (diagnose_trait_expr) > > > > > > <case CPTK_IS_LAYOUT_COMPATIBLE>: Explain why not. > > > > > > * cp-tree.h (layout_compatible_type_p): Add explain parameter. > > > > > > * typeck.cc (layout_compatible_type_p): Add explanations when > > > > > > returning false. > > > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > > > * g++.dg/cpp2a/is-layout-compatible4.C: New test. > > > > > > > > > > > > Signed-off-by: Nathaniel Shead <[email protected]> > > > > > > --- > > > > > > gcc/cp/constraint.cc | 3 +- > > > > > > gcc/cp/cp-tree.h | 2 +- > > > > > > gcc/cp/typeck.cc | 146 > > > > > > +++++++++++++++--- > > > > > > .../g++.dg/cpp2a/is-layout-compatible4.C | 86 +++++++++++ > > > > > > 4 files changed, 211 insertions(+), 26 deletions(-) > > > > > > create mode 100644 > > > > > > gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C > > > > > > > > > > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > > > > > > index 1ab5a2902d3..f55cae37007 100644 > > > > > > --- a/gcc/cp/constraint.cc > > > > > > +++ b/gcc/cp/constraint.cc > > > > > > @@ -3186,7 +3186,8 @@ diagnose_trait_expr (location_t loc, tree > > > > > > expr, tree args) > > > > > > } > > > > > > break; > > > > > > case CPTK_IS_LAYOUT_COMPATIBLE: > > > > > > - inform (loc, "%qT is not layout compatible with %qT", t1, > > > > > > t2); > > > > > > + inform (loc, "%qT is not layout compatible with %qT, > > > > > > because", t1, t2); > > > > > > + layout_compatible_type_p (t1, t2, /*explain=*/true); > > > > > > break; > > > > > > case CPTK_IS_LITERAL_TYPE: > > > > > > inform (decl_loc, "%qT is not a literal type", t1); > > > > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > > > > > > index 844dc3a577e..339ea062cd0 100644 > > > > > > --- a/gcc/cp/cp-tree.h > > > > > > +++ b/gcc/cp/cp-tree.h > > > > > > @@ -8550,7 +8550,7 @@ extern bool > > > > > > same_type_ignoring_top_level_qualifiers_p (tree, tree); > > > > > > extern bool similar_type_p (tree, tree); > > > > > > extern bool cp_comp_parm_types (tree, tree); > > > > > > extern bool next_common_initial_sequence (tree &, tree > > > > > > &); > > > > > > -extern bool layout_compatible_type_p (tree, tree); > > > > > > +extern bool layout_compatible_type_p (tree, tree, > > > > > > bool = false); > > > > > > extern bool compparms (const_tree, > > > > > > const_tree); > > > > > > extern int comp_cv_qualification (const_tree, > > > > > > const_tree); > > > > > > extern int comp_cv_qualification (int, int); > > > > > > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > > > > > > index dbadeb77085..97e96d0b045 100644 > > > > > > --- a/gcc/cp/typeck.cc > > > > > > +++ b/gcc/cp/typeck.cc > > > > > > @@ -1870,44 +1870,106 @@ next_common_initial_sequence (tree &memb1, > > > > > > tree &memb2) > > > > > > /* Return true if TYPE1 and TYPE2 are layout-compatible types. > > > > > > */ > > > > > > bool > > > > > > -layout_compatible_type_p (tree type1, tree type2) > > > > > > +layout_compatible_type_p (tree type1, tree type2, bool > > > > > > explain/*=false*/) > > > > > > { > > > > > > if (type1 == error_mark_node || type2 == error_mark_node) > > > > > > return false; > > > > > > if (type1 == type2) > > > > > > return true; > > > > > > - if (TREE_CODE (type1) != TREE_CODE (type2)) > > > > > > - return false; > > > > > > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED); > > > > > > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED); > > > > > > + if (same_type_p (type1, type2)) > > > > > > + return true; > > > > > > + > > > > > > + if (TREE_CODE (type1) != TREE_CODE (type2) > > > > > > + || (TREE_CODE (type1) != ENUMERAL_TYPE > > > > > > + && !CLASS_TYPE_P (type1)) > > > > > > + || (TREE_CODE (type2) != ENUMERAL_TYPE > > > > > > + && !CLASS_TYPE_P (type2))) > > > > > > + { > > > > > > + if (explain) > > > > > > + inform (input_location, "%q#T and %q#T are not both the same > > > > > > type, " > > > > > > + "layout-compatible enumerations, or " > > > > > > + "layout-compatible standard-layout class types", > > > > > > + type1, type2); > > > > > > + return false; > > > > > > + } > > > > > > if (TREE_CODE (type1) == ENUMERAL_TYPE) > > > > > > - return (tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE > > > > > > (type2)) > > > > > > - && same_type_p (finish_underlying_type (type1), > > > > > > - finish_underlying_type (type2))); > > > > > > + { > > > > > > + tree underlying1 = finish_underlying_type (type1); > > > > > > + tree underlying2 = finish_underlying_type (type2); > > > > > > + if (!same_type_p (underlying1, underlying2)) > > > > > > + { > > > > > > + if (explain) > > > > > > + { > > > > > > + inform (location_of (type1), > > > > > > + "the underlying type of %qT is %qT, but", > > > > > > + type1, underlying1); > > > > > > + inform (location_of (type2), > > > > > > + "the underlying type of %qT is %qT", > > > > > > + type2, underlying2); > > > > > > + } > > > > > > + return false; > > > > > > + } > > > > > > + } > > > > > > + else > > > > > > + gcc_checking_assert (CLASS_TYPE_P (type1) && CLASS_TYPE_P > > > > > > (type2)); > > > > > > - if (CLASS_TYPE_P (type1) > > > > > > - && std_layout_type_p (type1) > > > > > > - && std_layout_type_p (type2) > > > > > > - && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))) > > > > > > + if (!std_layout_type_p (type1)) > > > > > > + { > > > > > > + if (explain) > > > > > > + inform (location_of (type1), > > > > > > + "%qT is not a standard-layout type", type1); > > > > > > + return false; > > > > > > + } > > > > > > + if (!std_layout_type_p (type2)) > > > > > > + { > > > > > > + if (explain) > > > > > > + inform (location_of (type2), > > > > > > + "%qT is not a standard-layout type", type2); > > > > > > + return false; > > > > > > + } > > > > > > + > > > > > > + if (TREE_CODE (type1) == RECORD_TYPE) > > > > > > { > > > > > > tree field1 = TYPE_FIELDS (type1); > > > > > > tree field2 = TYPE_FIELDS (type2); > > > > > > - if (TREE_CODE (type1) == RECORD_TYPE) > > > > > > + while (1) > > > > > > { > > > > > > - while (1) > > > > > > + if (!next_common_initial_sequence (field1, field2)) > > > > > > { > > > > > > - if (!next_common_initial_sequence (field1, field2)) > > > > > > - return false; > > > > > > - if (field1 == NULL_TREE) > > > > > > - return true; > > > > > > - field1 = DECL_CHAIN (field1); > > > > > > - field2 = DECL_CHAIN (field2); > > > > > > + if (explain) > > > > > > + { > > > > > > + if (field1 && field2) > > > > > > + { > > > > > > + inform (DECL_SOURCE_LOCATION (field1), > > > > > > + "%qD and %qD do not correspond", > > > > > > + field1, field2); > > > > > > + inform (DECL_SOURCE_LOCATION (field2), > > > > > > + "%qD declared here", field2); > > > > > > + } > > > > > > + else if (field1) > > > > > > + inform (DECL_SOURCE_LOCATION (field1), > > > > > > + "%qT has no member corresponding to %qD", > > > > > > + type2, field1); > > > > > > + else if (field2) > > > > > > + inform (DECL_SOURCE_LOCATION (field2), > > > > > > + "%qT has no member corresponding to %qD", > > > > > > + type1, field2); > > > > > > + } > > > > > > + return false; > > > > > > } > > > > > > + if (field1 == NULL_TREE) > > > > > > + break; > > > > > > + field1 = DECL_CHAIN (field1); > > > > > > + field2 = DECL_CHAIN (field2); > > > > > > } > > > > > > - /* Otherwise both types must be union types. > > > > > > - The standard says: > > > > > > + } > > > > > > + else if (TREE_CODE (type1) == UNION_TYPE) > > > > > > + { > > > > > > + /* The standard says: > > > > > > "Two standard-layout unions are layout-compatible if > > > > > > they have > > > > > > the same number of non-static data members and > > > > > > corresponding > > > > > > non-static data members (in any order) have > > > > > > layout-compatible > > > > > > @@ -1915,6 +1977,8 @@ layout_compatible_type_p (tree type1, tree > > > > > > type2) > > > > > > but the code anticipates that bitfield vs. > > > > > > non-bitfield, > > > > > > different bitfield widths or presence/absence of > > > > > > [[no_unique_address]] should be checked as well. */ > > > > > > + tree field1 = TYPE_FIELDS (type1); > > > > > > + tree field2 = TYPE_FIELDS (type2); > > > > > > auto_vec<tree, 16> vec; > > > > > > unsigned int count = 0; > > > > > > for (; field1; field1 = DECL_CHAIN (field1)) > > > > > > @@ -1923,11 +1987,26 @@ layout_compatible_type_p (tree type1, tree > > > > > > type2) > > > > > > for (; field2; field2 = DECL_CHAIN (field2)) > > > > > > if (TREE_CODE (field2) == FIELD_DECL) > > > > > > vec.safe_push (field2); > > > > > > + > > > > > > /* Discussions on core lean towards treating multiple > > > > > > union fields > > > > > > of the same type as the same field, so this might need > > > > > > changing > > > > > > in the future. */ > > > > > > if (count != vec.length ()) > > > > > > - return false; > > > > > > + { > > > > > > + if (explain) > > > > > > + { > > > > > > + inform_n (location_of (type1), count, > > > > > > + "%qT has %u field, but", > > > > > > + "%qT has %u fields, but", > > > > > > + type1, count); > > > > > > + inform_n (location_of (type2), vec.length (), > > > > > > + "%qT has %u field", > > > > > > + "%qT has %u fields", > > > > > > + type2, vec.length ()); > > > > > > + } > > > > > > + return false; > > > > > > + } > > > > > > + > > > > > > for (field1 = TYPE_FIELDS (type1); field1; field1 = > > > > > > DECL_CHAIN (field1)) > > > > > > { > > > > > > if (TREE_CODE (field1) != FIELD_DECL) > > > > > > @@ -1961,13 +2040,32 @@ layout_compatible_type_p (tree type1, tree > > > > > > type2) > > > > > > break; > > > > > > } > > > > > > if (j == vec.length ()) > > > > > > - return false; > > > > > > + { > > > > > > + if (explain) > > > > > > + { > > > > > > + inform (DECL_SOURCE_LOCATION (field1), > > > > > > + "%qT has no member corresponding to %qD", > > > > > > + type2, field1); > > > > > > + inform (location_of (type2), "%qT declared here", > > > > > > type2); > > > > > > + } > > > > > > + return false; > > > > > > + } > > > > > > vec.unordered_remove (j); > > > > > > } > > > > > > - return true; > > > > > > } > > > > > > - return same_type_p (type1, type2); > > > > > > + if (!tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))) > > > > > > + { > > > > > > + if (explain) > > > > > > + { > > > > > > + inform (location_of (type1), "%qT and %qT have different > > > > > > sizes", > > > > > > + type1, type2); > > > > > > + inform (location_of (type2), "%qT declared here", type2); > > > > > > + } > > > > > > + return false; > > > > > > + } > > > > > > + > > > > > > + return true; > > > > > > } > > > > > > /* Returns 1 if TYPE1 is at least as qualified as TYPE2. */ > > > > > > diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C > > > > > > b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C > > > > > > new file mode 100644 > > > > > > index 00000000000..7ee5cbd6d07 > > > > > > --- /dev/null > > > > > > +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C > > > > > > @@ -0,0 +1,86 @@ > > > > > > +// Test for diagnostics on failed is_layout_compatible. > > > > > > +// { dg-do compile { target c++20 } } > > > > > > + > > > > > > +template <typename T, typename U> > > > > > > +constexpr bool is_layout_compatible_v = __is_layout_compatible (T, > > > > > > U); > > > > > > + > > > > > > +static_assert(is_layout_compatible_v<int, unsigned>); // { > > > > > > dg-error "assert" } > > > > > > +// { dg-message "is not layout compatible" "" { target *-*-* } .-1 > > > > > > } > > > > > > +// { dg-message "same type" "" { target *-*-* } .-2 } > > > > > > + > > > > > > +struct S {}; > > > > > > +static_assert(is_layout_compatible_v<const S, volatile int>); // > > > > > > { dg-error "assert" } > > > > > > +// { dg-message "is not layout compatible" "" { target *-*-* } .-1 > > > > > > } > > > > > > +// { dg-message "same type" "" { target *-*-* } .-2 } > > > > > > + > > > > > > +struct A { > > > > > > + int a; > > > > > > + char b; // { dg-message "'A::b' and 'B::b' do not correspond" } > > > > > > +}; > > > > > > +struct B { > > > > > > + int a; > > > > > > + signed char b; // { dg-message "declared here" } > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<A, B>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +struct C { > > > > > > + int : 1; > > > > > > + int c : 7; > > > > > > + int : 0; // { dg-message "'C::<anonymous>' and 'D::g' do not > > > > > > correspond" } > > > > > > + int : 2; > > > > > > +}; > > > > > > +struct D { > > > > > > + int f : 1; > > > > > > > > > > Hmm, I'm surprised that we consider named and unnamed bit-fields as > > > > > corresponding, but I guess the spec doesn't say otherwise. > > > > > > > > > > > + int : 7; > > > > > > + int g : 2; // { dg-message "declared here" } > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<C, D>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +struct E { // { dg-message "'E' is not a standard-layout type" } > > > > > > + int a; > > > > > > +private: > > > > > > + int b; > > > > > > +}; > > > > > > +struct F { > > > > > > + int a; > > > > > > +private: > > > > > > + int b; > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<E, F>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +union G { > > > > > > + int a; > > > > > > + long long b; > > > > > > + signed char c; // { dg-message "'H' has no member corresponding > > > > > > to 'G::c'" } > > > > > > +}; > > > > > > +union H { // { dg-message "declared here" } > > > > > > + char x; > > > > > > + int y; > > > > > > + long long z; > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<G, H>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +union I { // { dg-message "'I' has 2 fields, but" } > > > > > > + int a; > > > > > > + double b; > > > > > > +}; > > > > > > +union J { // { dg-message "'J' has 1 field" } > > > > > > + int c; > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<I, J>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +enum K : int { // { dg-message "the underlying type of 'K' is > > > > > > 'int'" } > > > > > > + K0, K1 > > > > > > +}; > > > > > > +enum L : long int { // { dg-message "the underlying type of 'L' > > > > > > is 'long int'" } > > > > > > + L0, L1 > > > > > > +}; > > > > > > +static_assert(is_layout_compatible_v<K, L>); // { dg-error > > > > > > "assert" } > > > > > > + > > > > > > +struct M { // { dg-message "different sizes" } > > > > > > > > > > I think it would be clearer to refer to the alignment requirements. > > > > > > > > > > Jason > > > > > > > > > > > > > The standard isn't really explicit about behaviour of different > > > > alignment requirements for the top-level type, that I can see; but I > > > > think it does make sense that differently sized types should not be > > > > considered layout-compatible, and so this clarifies the note for when > > > > the different sizes are caused by alignment differences. > > > > > > But it does require that corresponding members have the same alignment > > > requirements, and considering the alignment of the type as a whole seems > > > natural; the alignment requirement for the type is effectively an > > > alignment > > > requirement for the first member. > > > > > > So I'd prefer to just talk about the alignment if that's different, > > > without > > > mentioning that it affects the size. > > > > > > > To clarify, keep the logic as-is and just change the message, or do > > something like this and actually change the checks? > > > > if (TYPE_ALIGN (type1) != TYPE_ALIGN (type2)) > > { > > if (explain) > > { > > inform (location_of (type1), > > "%qT and %qT have different alignment requirements", > > type1, type2); > > inform (location_of (type2), "%qT declared here", type2); > > } > > return false; > > } > > > > if (!tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))) > > { > > if (explain) > > { > > inform (location_of (type1), > > "%qT and %qT have different sizes", type1, type2); > > inform (location_of (type2), "%qT declared here", type2); > > } > > return false; > > } > > > > This will change the behaviour of whether say > > > > struct A { char x[8]; }; > > struct alignas(8) B { char x[8]; }; > > > > are considered layout compatible; we currently say that they are but it > > sounds like maybe we want to interpret them as not being so? > > That seemed intuitive to me from the words "layout-compatible", since they > have different layout effects when used as member types. > > But the actual wording seems pretty clear that we only consider the members, > ignoring both alignment and size of the class itself. And Clang and MSVC > seem to follow that, unlike current GCC: https://godbolt.org/z/9GYYY9e7z > > So on reflection I think rather than add an alignment comparison, we want to > drop the size comparison. That should probably be a separate patch from > this diagnostic improvement, though. > > Jason >
Thanks, makes sense to me. Here are two separate patches, one to drop the size comparison and then an update for this patch rebased on top of that one. Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
>From bc28e3fbc58e8fb00cd9376433f6264d5fbba791 Mon Sep 17 00:00:00 2001 From: Nathaniel Shead <[email protected]> Date: Sat, 22 Nov 2025 22:11:35 +1100 Subject: [PATCH 1/2] c++: Correct behaviour of layout_compatible_type for aligned types The standard does not require two types to have the same alignment (and hence size) to be considered layout-compatible. The same applies to members of unions. gcc/cp/ChangeLog: * typeck.cc (layout_compatible_type_p): Do not check TYPE_SIZE. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/is-layout-compatible3.C: Adjust expected results. Signed-off-by: Nathaniel Shead <[email protected]> --- gcc/cp/typeck.cc | 8 +++----- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 2ab45f3fff6..0b8ef84bf50 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -1883,14 +1883,12 @@ layout_compatible_type_p (tree type1, tree type2) type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED); if (TREE_CODE (type1) == ENUMERAL_TYPE) - return (tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2)) - && same_type_p (finish_underlying_type (type1), - finish_underlying_type (type2))); + return same_type_p (finish_underlying_type (type1), + finish_underlying_type (type2)); if (CLASS_TYPE_P (type1) && std_layout_type_p (type1) - && std_layout_type_p (type2) - && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))) + && std_layout_type_p (type2)) { tree field1 = TYPE_FIELDS (type1); tree field2 = TYPE_FIELDS (type2); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C index 499ba4977d4..e48679fef11 100644 --- a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C @@ -51,15 +51,15 @@ struct B1 { signed int b; }; struct alignas (16) C1 : public A1 {}; struct alignas (16) D1 : public B1 {}; -static_assert (!std::is_layout_compatible_v<I, J>); +static_assert (std::is_layout_compatible_v<I, J>); static_assert (!std::is_layout_compatible_v<K, L>); -static_assert (!std::is_layout_compatible_v<M, N>); +static_assert (std::is_layout_compatible_v<M, N>); static_assert (!std::is_layout_compatible_v<O, P>); static_assert (!std::is_layout_compatible_v<P, D>); static_assert (std::is_layout_compatible_v<Q, R>); static_assert (!std::is_layout_compatible_v<U, V>); -static_assert (!std::is_layout_compatible_v<A, I>); -static_assert (!std::is_layout_compatible_v<C, I>); +static_assert (std::is_layout_compatible_v<A, I>); +static_assert (std::is_layout_compatible_v<C, I>); static_assert (!std::is_layout_compatible_v<E, F>); static_assert (std::is_layout_compatible_v<G, H>); static_assert (std::is_layout_compatible_v<C1, D1>); -- 2.51.0
>From 082c5f2866747b4e2158e8887aa4daeb21cfd14a Mon Sep 17 00:00:00 2001 From: Nathaniel Shead <[email protected]> Date: Thu, 23 Oct 2025 13:57:37 +1100 Subject: [PATCH 2/2] c++: Add detailed diagnostics for __is_layout_compatible gcc/cp/ChangeLog: * constraint.cc (diagnose_trait_expr) <case CPTK_IS_LAYOUT_COMPATIBLE>: Explain why not. * cp-tree.h (layout_compatible_type_p): Add explain parameter. * typeck.cc (layout_compatible_type_p): Add explanations when returning false. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/is-layout-compatible4.C: New test. Signed-off-by: Nathaniel Shead <[email protected]> --- gcc/cp/constraint.cc | 3 +- gcc/cp/cp-tree.h | 2 +- gcc/cp/typeck.cc | 134 +++++++++++++++--- .../g++.dg/cpp2a/is-layout-compatible4.C | 78 ++++++++++ 4 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index ebc3b8a4020..19d2d8f82d5 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3188,7 +3188,8 @@ diagnose_trait_expr (location_t loc, tree expr, tree args) } break; case CPTK_IS_LAYOUT_COMPATIBLE: - inform (loc, "%qT is not layout compatible with %qT", t1, t2); + inform (loc, "%qT is not layout compatible with %qT, because", t1, t2); + layout_compatible_type_p (t1, t2, /*explain=*/true); break; case CPTK_IS_LITERAL_TYPE: inform (decl_loc, "%qT is not a literal type", t1); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d4e7f10d25e..9e8d576a15b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8534,7 +8534,7 @@ extern bool same_type_ignoring_top_level_qualifiers_p (tree, tree); extern bool similar_type_p (tree, tree); extern bool cp_comp_parm_types (tree, tree); extern bool next_common_initial_sequence (tree &, tree &); -extern bool layout_compatible_type_p (tree, tree); +extern bool layout_compatible_type_p (tree, tree, bool = false); extern bool compparms (const_tree, const_tree); extern int comp_cv_qualification (const_tree, const_tree); extern int comp_cv_qualification (int, int); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 0b8ef84bf50..65e08eaa645 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -1870,42 +1870,103 @@ next_common_initial_sequence (tree &memb1, tree &memb2) /* Return true if TYPE1 and TYPE2 are layout-compatible types. */ bool -layout_compatible_type_p (tree type1, tree type2) +layout_compatible_type_p (tree type1, tree type2, bool explain/*=false*/) { if (type1 == error_mark_node || type2 == error_mark_node) return false; if (type1 == type2) return true; - if (TREE_CODE (type1) != TREE_CODE (type2)) - return false; type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED); type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED); + if (same_type_p (type1, type2)) + return true; - if (TREE_CODE (type1) == ENUMERAL_TYPE) - return same_type_p (finish_underlying_type (type1), - finish_underlying_type (type2)); + if (TREE_CODE (type1) != TREE_CODE (type2) + || (TREE_CODE (type1) != ENUMERAL_TYPE + && !CLASS_TYPE_P (type1)) + || (TREE_CODE (type2) != ENUMERAL_TYPE + && !CLASS_TYPE_P (type2))) + { + if (explain) + inform (input_location, "%q#T and %q#T are not both the same type, " + "layout-compatible enumerations, or " + "layout-compatible standard-layout class types", + type1, type2); + return false; + } + + if (!std_layout_type_p (type1)) + { + if (explain) + inform (location_of (type1), + "%qT is not a standard-layout type", type1); + return false; + } + if (!std_layout_type_p (type2)) + { + if (explain) + inform (location_of (type2), + "%qT is not a standard-layout type", type2); + return false; + } - if (CLASS_TYPE_P (type1) - && std_layout_type_p (type1) - && std_layout_type_p (type2)) + if (TREE_CODE (type1) == ENUMERAL_TYPE) + { + tree underlying1 = finish_underlying_type (type1); + tree underlying2 = finish_underlying_type (type2); + if (!same_type_p (underlying1, underlying2)) + { + if (explain) + { + inform (location_of (type1), + "the underlying type of %qT is %qT, but", + type1, underlying1); + inform (location_of (type2), + "the underlying type of %qT is %qT", + type2, underlying2); + } + return false; + } + } + else if (TREE_CODE (type1) == RECORD_TYPE) { tree field1 = TYPE_FIELDS (type1); tree field2 = TYPE_FIELDS (type2); - if (TREE_CODE (type1) == RECORD_TYPE) + while (1) { - while (1) + if (!next_common_initial_sequence (field1, field2)) { - if (!next_common_initial_sequence (field1, field2)) - return false; - if (field1 == NULL_TREE) - return true; - field1 = DECL_CHAIN (field1); - field2 = DECL_CHAIN (field2); + if (explain) + { + if (field1 && field2) + { + inform (DECL_SOURCE_LOCATION (field1), + "%qD and %qD do not correspond", + field1, field2); + inform (DECL_SOURCE_LOCATION (field2), + "%qD declared here", field2); + } + else if (field1) + inform (DECL_SOURCE_LOCATION (field1), + "%qT has no member corresponding to %qD", + type2, field1); + else if (field2) + inform (DECL_SOURCE_LOCATION (field2), + "%qT has no member corresponding to %qD", + type1, field2); + } + return false; } + if (field1 == NULL_TREE) + break; + field1 = DECL_CHAIN (field1); + field2 = DECL_CHAIN (field2); } - /* Otherwise both types must be union types. - The standard says: + } + else if (TREE_CODE (type1) == UNION_TYPE) + { + /* The standard says: "Two standard-layout unions are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in any order) have layout-compatible @@ -1913,6 +1974,8 @@ layout_compatible_type_p (tree type1, tree type2) but the code anticipates that bitfield vs. non-bitfield, different bitfield widths or presence/absence of [[no_unique_address]] should be checked as well. */ + tree field1 = TYPE_FIELDS (type1); + tree field2 = TYPE_FIELDS (type2); auto_vec<tree, 16> vec; unsigned int count = 0; for (; field1; field1 = DECL_CHAIN (field1)) @@ -1921,11 +1984,26 @@ layout_compatible_type_p (tree type1, tree type2) for (; field2; field2 = DECL_CHAIN (field2)) if (TREE_CODE (field2) == FIELD_DECL) vec.safe_push (field2); + /* Discussions on core lean towards treating multiple union fields of the same type as the same field, so this might need changing in the future. */ if (count != vec.length ()) - return false; + { + if (explain) + { + inform_n (location_of (type1), count, + "%qT has %u field, but", + "%qT has %u fields, but", + type1, count); + inform_n (location_of (type2), vec.length (), + "%qT has %u field", + "%qT has %u fields", + type2, vec.length ()); + } + return false; + } + for (field1 = TYPE_FIELDS (type1); field1; field1 = DECL_CHAIN (field1)) { if (TREE_CODE (field1) != FIELD_DECL) @@ -1959,13 +2037,23 @@ layout_compatible_type_p (tree type1, tree type2) break; } if (j == vec.length ()) - return false; + { + if (explain) + { + inform (DECL_SOURCE_LOCATION (field1), + "%qT has no member corresponding to %qD", + type2, field1); + inform (location_of (type2), "%qT declared here", type2); + } + return false; + } vec.unordered_remove (j); } - return true; } + else + gcc_unreachable (); - return same_type_p (type1, type2); + return true; } /* Returns 1 if TYPE1 is at least as qualified as TYPE2. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C new file mode 100644 index 00000000000..6217fcbcff2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible4.C @@ -0,0 +1,78 @@ +// Test for diagnostics on failed is_layout_compatible. +// { dg-do compile { target c++20 } } + +template <typename T, typename U> +constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U); + +static_assert(is_layout_compatible_v<int, unsigned>); // { dg-error "assert" } +// { dg-message "is not layout compatible" "" { target *-*-* } .-1 } +// { dg-message "same type" "" { target *-*-* } .-2 } + +struct S {}; +static_assert(is_layout_compatible_v<const S, volatile int>); // { dg-error "assert" } +// { dg-message "is not layout compatible" "" { target *-*-* } .-1 } +// { dg-message "same type" "" { target *-*-* } .-2 } + +struct A { + int a; + char b; // { dg-message "'A::b' and 'B::b' do not correspond" } +}; +struct B { + int a; + signed char b; // { dg-message "declared here" } +}; +static_assert(is_layout_compatible_v<A, B>); // { dg-error "assert" } + +struct C { + int : 1; + int c : 7; + int : 0; // { dg-message "'C::<anonymous>' and 'D::g' do not correspond" } + int : 2; +}; +struct D { + int f : 1; + int : 7; + int g : 2; // { dg-message "declared here" } +}; +static_assert(is_layout_compatible_v<C, D>); // { dg-error "assert" } + +struct E { // { dg-message "'E' is not a standard-layout type" } + int a; +private: + int b; +}; +struct F { + int a; +private: + int b; +}; +static_assert(is_layout_compatible_v<E, F>); // { dg-error "assert" } + +union G { + int a; + long long b; + signed char c; // { dg-message "'H' has no member corresponding to 'G::c'" } +}; +union H { // { dg-message "declared here" } + char x; + int y; + long long z; +}; +static_assert(is_layout_compatible_v<G, H>); // { dg-error "assert" } + +union I { // { dg-message "'I' has 2 fields, but" } + int a; + double b; +}; +union J { // { dg-message "'J' has 1 field" } + int c; +}; +static_assert(is_layout_compatible_v<I, J>); // { dg-error "assert" } + +enum K : int { // { dg-message "the underlying type of 'K' is 'int'" } + K0, K1 +}; +enum L : long int { // { dg-message "the underlying type of 'L' is 'long int'" } + L0, L1 +}; +static_assert(is_layout_compatible_v<K, L>); // { dg-error "assert" } -- 2.51.0
