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.

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