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?

Nathaniel

> > I'm not sure if it's ever possible to have different sizes for any
> > reasons *other* than alignment by this point, but I've left that message
> > in for completeness in case there's some attribute (now or later) that
> > might affect this.
> 
> Sure, that's fine.
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > 
> > -- >8 --
> > 
> > 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                              | 152 +++++++++++++++---
> >   .../g++.dg/cpp2a/is-layout-compatible4.C      |  86 ++++++++++
> >   4 files changed, 217 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 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 2ab45f3fff6..21aa2a59dd9 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,38 @@ 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)
> > +   {
> > +     if (TYPE_ALIGN (type1) != TYPE_ALIGN (type2))
> > +       inform (location_of (type1),
> > +               "%qT and %qT have alignment requirements "
> > +               "resulting in different sizes",
> > +               type1, type2);
> > +     else
> > +       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..542ef6f842f
> > --- /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;
> > +  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 "alignment" }
> > +  int x;
> > +};
> > +struct alignas(16) N {  // { dg-message "declared here" }
> > +  int y;
> > +};
> > +static_assert(is_layout_compatible_v<M, N>);  // { dg-error "assert" }
> 

Reply via email to