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.
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.
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" }
--
2.51.0