On Mon, Aug 16, 2021 at 03:57:21PM -0400, Jason Merrill wrote:
> > +static tree
> > +is_corresponding_member_aggr (location_t loc, tree basetype1, tree 
> > membertype1,
> > +                         tree arg1, tree basetype2, tree membertype2,
> > +                         tree arg2)
> > +{
> > +  tree field1 = TYPE_FIELDS (basetype1);
> > +  tree field2 = TYPE_FIELDS (basetype2);
> > +  tree ret = boolean_false_node;
> > +  while (1)
> > +    {
> 
> Can we share more of the code between this function and
> layout_compatible_type_p?  I'm thinking of a function something like
> 
> bool next_common_initial_seqence (tree &mem1, tree &mem2)
> 
> that would update mem1/mem2 to the next entities of the common initial
> sequence (or null at the end) and return true, or return false if the next
> fields are not compatible.

Ok, here it is, tested with make check-c++-all RUNTESTFLAGS=dg.exp=cpp2a/*
so far (full bootstrap/regtests are scheduled).

I had to repeat in the anonymous struct is_corresponding_member_aggr case
the [[no_unique_address]] and bit_position checks, because we want to
recurse into the anonymous structs even when they aren't layout compatible
but just have some non-empty common initial sequence.
And the old code for non-empty DECL_FIELD_IS_BASE recursed, while the
new function just continues iterating on TYPE_FIELDS of the base.
That changes behavior of (added to the test):
struct A1 { int a; };
struct B1 { signed int b; };
struct alignas (16) C1 : public A1 {};
struct alignas (16) D1 : public B1 {};
static_assert (std::is_layout_compatible_v<C1, D1>);
which failed the assertion previously (because it temporarily tested if
A1 is layout compatible with D1, which it is not), but that actually is
a good thing, I think C1 and D1 are clearly layout compatible.
Or (not in the testsuite):
struct A1 { int a; };
struct B1 { signed int b; };
struct alignas (8) E1 : public A1 {};
struct F1 : public B1 {};
struct alignas (16) G1 : public E1 {};
struct alignas (16) H1 : public F1 {};
static_assert (std::is_layout_compatible_v<A1, B1>);
static_assert (!std::is_layout_compatible_v<E1, F1>);
static_assert (std::is_layout_compatible_v<G1, H1>);
(previously the last assertion would fail).

2021-08-17  Jakub Jelinek  <ja...@redhat.com>

        PR c++/101539
gcc/c-family/
        * c-common.h (enum rid): Add RID_IS_LAYOUT_COMPATIBLE.
        * c-common.c (c_common_reswords): Add __is_layout_compatible.
gcc/cp/
        * cp-tree.h (enum cp_trait_kind): Add CPTK_IS_LAYOUT_COMPATIBLE.
        (enum cp_built_in_function): Add CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
        (fold_builtin_is_corresponding_member, next_common_initial_seqence,
        layout_compatible_type_p): Declare.
        * parser.c (cp_parser_primary_expression): Handle
        RID_IS_LAYOUT_COMPATIBLE.
        (cp_parser_trait_expr): Likewise.
        * cp-objcp-common.c (names_builtin_p): Likewise.
        * constraint.cc (diagnose_trait_expr): Handle
        CPTK_IS_LAYOUT_COMPATIBLE.
        * decl.c (cxx_init_decl_processing): Register
        __builtin_is_corresponding_member builtin.
        * constexpr.c (cxx_eval_builtin_function_call): Handle
        CP_BUILT_IN_IS_CORRESPONDING_MEMBER builtin.
        * semantics.c (is_corresponding_member_union,
        is_corresponding_member_aggr, fold_builtin_is_corresponding_member):
        New functions.
        (trait_expr_value): Handle CPTK_IS_LAYOUT_COMPATIBLE.
        (finish_trait_expr): Likewise.
        * typeck.c (next_common_initial_seqence, layout_compatible_type_p):
        New functions.
        * cp-gimplify.c (cp_gimplify_expr): Fold
        CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
        (cp_fold): Likewise.
        * tree.c (builtin_valid_in_constant_expr_p): Handle
        CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
        * cxx-pretty-print.c (pp_cxx_trait_expression): Handle
        CPTK_IS_LAYOUT_COMPATIBLE.
        * class.c (remove_zero_width_bit_fields): Remove.
        (layout_class_type): Don't call it.
gcc/testsuite/
        * g++.dg/cpp2a/is-corresponding-member1.C: New test.
        * g++.dg/cpp2a/is-corresponding-member2.C: New test.
        * g++.dg/cpp2a/is-corresponding-member3.C: New test.
        * g++.dg/cpp2a/is-corresponding-member4.C: New test.
        * g++.dg/cpp2a/is-corresponding-member5.C: New test.
        * g++.dg/cpp2a/is-corresponding-member6.C: New test.
        * g++.dg/cpp2a/is-corresponding-member7.C: New test.
        * g++.dg/cpp2a/is-corresponding-member8.C: New test.
        * g++.dg/cpp2a/is-layout-compatible1.C: New test.
        * g++.dg/cpp2a/is-layout-compatible2.C: New test.
        * g++.dg/cpp2a/is-layout-compatible3.C: New test.

--- gcc/c-family/c-common.h.jj  2021-08-12 22:40:49.040646930 +0200
+++ gcc/c-family/c-common.h     2021-08-17 10:51:16.976465135 +0200
@@ -173,7 +173,8 @@ enum rid
   RID_IS_ABSTRACT,             RID_IS_AGGREGATE,
   RID_IS_BASE_OF,              RID_IS_CLASS,
   RID_IS_EMPTY,                RID_IS_ENUM,
-  RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
+  RID_IS_FINAL,                RID_IS_LAYOUT_COMPATIBLE,
+  RID_IS_LITERAL_TYPE,
   RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
   RID_IS_POD,                  RID_IS_POLYMORPHIC,
   RID_IS_SAME_AS,
--- gcc/c-family/c-common.c.jj  2021-08-12 22:36:44.617004468 +0200
+++ gcc/c-family/c-common.c     2021-08-17 10:51:16.978465108 +0200
@@ -420,6 +420,7 @@ const struct c_common_resword c_common_r
   { "__is_empty",      RID_IS_EMPTY,   D_CXXONLY },
   { "__is_enum",       RID_IS_ENUM,    D_CXXONLY },
   { "__is_final",      RID_IS_FINAL,   D_CXXONLY },
+  { "__is_layout_compatible", RID_IS_LAYOUT_COMPATIBLE, D_CXXONLY },
   { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
   { "__is_pointer_interconvertible_base_of",
                        RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY },
--- gcc/cp/cp-tree.h.jj 2021-08-12 22:36:44.680003603 +0200
+++ gcc/cp/cp-tree.h    2021-08-17 11:04:38.041528724 +0200
@@ -1365,6 +1365,7 @@ enum cp_trait_kind
   CPTK_IS_EMPTY,
   CPTK_IS_ENUM,
   CPTK_IS_FINAL,
+  CPTK_IS_LAYOUT_COMPATIBLE,
   CPTK_IS_LITERAL_TYPE,
   CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
   CPTK_IS_POD,
@@ -6358,6 +6359,7 @@ struct GTY((chain_next ("%h.next"))) tin
 enum cp_built_in_function {
   CP_BUILT_IN_IS_CONSTANT_EVALUATED,
   CP_BUILT_IN_INTEGER_PACK,
+  CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
   CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
   CP_BUILT_IN_SOURCE_LOCATION,
   CP_BUILT_IN_LAST
@@ -7574,6 +7576,7 @@ extern tree baselink_for_fns
 extern void finish_static_assert                (tree, tree, location_t,
                                                 bool, bool);
 extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
+extern tree fold_builtin_is_corresponding_member (location_t, int, tree *);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, 
int, tree *);
 extern tree finish_trait_expr                  (location_t, enum 
cp_trait_kind, tree, tree);
 extern tree build_lambda_expr                   (void);
@@ -7800,6 +7803,8 @@ extern bool comp_except_specs                     (const_t
 extern bool comptypes                          (tree, tree, int);
 extern bool same_type_ignoring_top_level_qualifiers_p (tree, tree);
 extern bool similar_type_p                     (tree, tree);
+extern bool next_common_initial_seqence                (tree &, tree &);
+extern bool layout_compatible_type_p           (tree, tree);
 extern bool compparms                          (const_tree, const_tree);
 extern int comp_cv_qualification               (const_tree, const_tree);
 extern int comp_cv_qualification               (int, int);
--- gcc/cp/parser.c.jj  2021-08-17 09:29:41.405204939 +0200
+++ gcc/cp/parser.c     2021-08-17 10:51:16.983465040 +0200
@@ -5816,6 +5816,7 @@ cp_parser_primary_expression (cp_parser
        case RID_IS_EMPTY:
        case RID_IS_ENUM:
        case RID_IS_FINAL:
+       case RID_IS_LAYOUT_COMPATIBLE:
        case RID_IS_LITERAL_TYPE:
        case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
        case RID_IS_POD:
@@ -10707,6 +10708,10 @@ cp_parser_trait_expr (cp_parser* parser,
     case RID_IS_FINAL:
       kind = CPTK_IS_FINAL;
       break;
+    case RID_IS_LAYOUT_COMPATIBLE:
+      kind = CPTK_IS_LAYOUT_COMPATIBLE;
+      binary = true;
+      break;
     case RID_IS_LITERAL_TYPE:
       kind = CPTK_IS_LITERAL_TYPE;
       break;
--- gcc/cp/cp-objcp-common.c.jj 2021-08-12 22:36:44.678003631 +0200
+++ gcc/cp/cp-objcp-common.c    2021-08-17 10:51:16.984465026 +0200
@@ -413,6 +413,7 @@ names_builtin_p (const char *name)
     case RID_IS_EMPTY:
     case RID_IS_ENUM:
     case RID_IS_FINAL:
+    case RID_IS_LAYOUT_COMPATIBLE:
     case RID_IS_LITERAL_TYPE:
     case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
     case RID_IS_POD:
--- gcc/cp/constraint.cc.jj     2021-08-12 22:36:44.677003644 +0200
+++ gcc/cp/constraint.cc        2021-08-17 10:51:16.984465026 +0200
@@ -3628,6 +3628,9 @@ diagnose_trait_expr (tree expr, tree arg
     case CPTK_IS_FINAL:
       inform (loc, "  %qT is not a final class", t1);
       break;
+    case CPTK_IS_LAYOUT_COMPATIBLE:
+      inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
+      break;
     case CPTK_IS_LITERAL_TYPE:
       inform (loc, "  %qT is not a literal type", t1);
       break;
--- gcc/cp/decl.c.jj    2021-08-12 22:36:44.712003163 +0200
+++ gcc/cp/decl.c       2021-08-17 10:51:16.986464998 +0200
@@ -4470,6 +4470,13 @@ cxx_init_decl_processing (void)
   tree bool_vaftype = build_varargs_function_type_list (boolean_type_node,
                                                        NULL_TREE);
   decl
+    = add_builtin_function ("__builtin_is_corresponding_member",
+                           bool_vaftype,
+                           CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
+                           BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+
+  decl
     = add_builtin_function ("__builtin_is_pointer_interconvertible_with_class",
                            bool_vaftype,
                            CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
--- gcc/cp/constexpr.c.jj       2021-08-12 22:36:44.677003644 +0200
+++ gcc/cp/constexpr.c  2021-08-17 10:51:16.986464998 +0200
@@ -1438,6 +1438,18 @@ cxx_eval_builtin_function_call (const co
        = fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs,
                                                               args);
     }
+  else if (fndecl_built_in_p (fun,
+                             CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
+                             BUILT_IN_FRONTEND))
+    {
+      location_t loc = EXPR_LOCATION (t);
+      if (nargs >= 2)
+       {
+         VERIFY_CONSTANT (args[0]);
+         VERIFY_CONSTANT (args[1]);
+       }
+      new_call = fold_builtin_is_corresponding_member (loc, nargs, args);
+    }
   else
     new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
                                        CALL_EXPR_FN (t), nargs, args);
--- gcc/cp/semantics.c.jj       2021-08-12 22:40:49.048646820 +0200
+++ gcc/cp/semantics.c  2021-08-17 11:36:44.024227609 +0200
@@ -10693,6 +10693,248 @@ fold_builtin_is_pointer_inverconvertible
                      build_zero_cst (TREE_TYPE (arg)));
 }
 
+/* Helper function for is_corresponding_member_aggr.  Return true if
+   MEMBERTYPE pointer-to-data-member ARG can be found in anonymous
+   union or structure BASETYPE.  */
+
+static bool
+is_corresponding_member_union (tree basetype, tree membertype, tree arg)
+{
+  for (tree field = TYPE_FIELDS (basetype); field; field = DECL_CHAIN (field))
+    if (TREE_CODE (field) != FIELD_DECL || DECL_BIT_FIELD_TYPE (field))
+      continue;
+    else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field),
+                                                       membertype))
+      {
+       if (TREE_CODE (arg) != INTEGER_CST
+           || tree_int_cst_equal (arg, byte_position (field)))
+         return true;
+      }
+    else if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
+      {
+       tree narg = arg;
+       if (TREE_CODE (basetype) != UNION_TYPE
+           && TREE_CODE (narg) == INTEGER_CST)
+         narg = size_binop (MINUS_EXPR, arg, byte_position (field));
+       if (is_corresponding_member_union (TREE_TYPE (field),
+                                          membertype, narg))
+         return true;
+      }
+  return false;
+}
+
+/* Helper function for fold_builtin_is_corresponding_member call.
+   Return boolean_false_node if MEMBERTYPE1 BASETYPE1::*ARG1 and
+   MEMBERTYPE2 BASETYPE2::*ARG2 aren't corresponding members,
+   boolean_true_node if they are corresponding members, or for
+   non-constant ARG2 the highest member offset for corresponding
+   members.  */
+
+static tree
+is_corresponding_member_aggr (location_t loc, tree basetype1, tree membertype1,
+                             tree arg1, tree basetype2, tree membertype2,
+                             tree arg2)
+{
+  tree field1 = TYPE_FIELDS (basetype1);
+  tree field2 = TYPE_FIELDS (basetype2);
+  tree ret = boolean_false_node;
+  while (1)
+    {
+      bool r = next_common_initial_seqence (field1, field2);
+      if (field1 == NULL_TREE || field2 == NULL_TREE)
+       break;
+      if (r
+         && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field1),
+                                                       membertype1)
+         && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field2),
+                                                       membertype2))
+       {
+         tree pos = byte_position (field1);
+         if (TREE_CODE (arg1) == INTEGER_CST
+             && tree_int_cst_equal (arg1, pos))
+           {
+             if (TREE_CODE (arg2) == INTEGER_CST)
+               return boolean_true_node;
+             return pos;
+           }
+         else if (TREE_CODE (arg1) != INTEGER_CST)
+           ret = pos;
+       }
+      else if (ANON_AGGR_TYPE_P (TREE_TYPE (field1))
+              && ANON_AGGR_TYPE_P (TREE_TYPE (field2)))
+       {
+         if ((!lookup_attribute ("no_unique_address",
+                                 DECL_ATTRIBUTES (field1)))
+             != !lookup_attribute ("no_unique_address",
+                                   DECL_ATTRIBUTES (field2)))
+           break;
+         if (!tree_int_cst_equal (bit_position (field1),
+                                  bit_position (field2)))
+           break;
+         bool overlap = true;
+         tree pos = byte_position (field1);
+         if (TREE_CODE (arg1) == INTEGER_CST)
+           {
+             tree off1 = fold_convert (sizetype, arg1);
+             tree sz1 = TYPE_SIZE_UNIT (TREE_TYPE (field1));
+             if (tree_int_cst_lt (off1, pos)
+                 || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz1), off1))
+               overlap = false;
+           }
+         if (TREE_CODE (arg2) == INTEGER_CST)
+           {
+             tree off2 = fold_convert (sizetype, arg2);
+             tree sz2 = TYPE_SIZE_UNIT (TREE_TYPE (field2));
+             if (tree_int_cst_lt (off2, pos)
+                 || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz2), off2))
+               overlap = false;
+           }
+         if (overlap
+             && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field1))
+             && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field2)))
+           {
+             tree narg1 = arg1;
+             if (TREE_CODE (arg1) == INTEGER_CST)
+               narg1 = size_binop (MINUS_EXPR,
+                                   fold_convert (sizetype, arg1), pos);
+             tree narg2 = arg2;
+             if (TREE_CODE (arg2) == INTEGER_CST)
+               narg2 = size_binop (MINUS_EXPR,
+                                   fold_convert (sizetype, arg2), pos);
+             tree t1 = TREE_TYPE (field1);
+             tree t2 = TREE_TYPE (field2);
+             tree nret = is_corresponding_member_aggr (loc, t1, membertype1,
+                                                       narg1, t2, membertype2,
+                                                       narg2);
+             if (nret != boolean_false_node)
+               {
+                 if (nret == boolean_true_node)
+                   return nret;
+                 if (TREE_CODE (arg1) == INTEGER_CST)
+                   return size_binop (PLUS_EXPR, nret, pos);
+                 ret = size_binop (PLUS_EXPR, nret, pos);
+               }
+           }
+         else if (overlap
+                  && TREE_CODE (TREE_TYPE (field1)) == UNION_TYPE
+                  && TREE_CODE (TREE_TYPE (field2)) == UNION_TYPE)
+           {
+             tree narg1 = arg1;
+             if (TREE_CODE (arg1) == INTEGER_CST)
+               narg1 = size_binop (MINUS_EXPR,
+                                   fold_convert (sizetype, arg1), pos);
+             tree narg2 = arg2;
+             if (TREE_CODE (arg2) == INTEGER_CST)
+               narg2 = size_binop (MINUS_EXPR,
+                                   fold_convert (sizetype, arg2), pos);
+             if (is_corresponding_member_union (TREE_TYPE (field1),
+                                                membertype1, narg1)
+                 && is_corresponding_member_union (TREE_TYPE (field2),
+                                                   membertype2, narg2))
+               {
+                 sorry_at (loc, "%<__builtin_is_corresponding_member%> "
+                                "not well defined for anonymous unions");
+                 return boolean_false_node;
+               }
+           }
+       }
+      if (!r)
+       break;
+      field1 = DECL_CHAIN (field1);
+      field2 = DECL_CHAIN (field2);
+    }
+  return ret;
+}
+
+/* Fold __builtin_is_corresponding_member call.  */
+
+tree
+fold_builtin_is_corresponding_member (location_t loc, int nargs,
+                                     tree *args)
+{
+  /* Unless users call the builtin directly, the following 3 checks should be
+     ensured from std::is_corresponding_member function template.  */
+  if (nargs != 2)
+    {
+      error_at (loc, "%<__builtin_is_corresponding_member%> "
+                    "needs two arguments");
+      return boolean_false_node;
+    }
+  tree arg1 = args[0];
+  tree arg2 = args[1];
+  if (error_operand_p (arg1) || error_operand_p (arg2))
+    return boolean_false_node;
+  if (!TYPE_PTRMEM_P (TREE_TYPE (arg1))
+      || !TYPE_PTRMEM_P (TREE_TYPE (arg2)))
+    {
+      error_at (loc, "%<__builtin_is_corresponding_member%> "
+                    "argument is not pointer to member");
+      return boolean_false_node;
+    }
+
+  if (!TYPE_PTRDATAMEM_P (TREE_TYPE (arg1))
+      || !TYPE_PTRDATAMEM_P (TREE_TYPE (arg2)))
+    return boolean_false_node;
+
+  tree membertype1 = TREE_TYPE (TREE_TYPE (arg1));
+  tree basetype1 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg1));
+  if (!complete_type_or_else (basetype1, NULL_TREE))
+    return boolean_false_node;
+
+  tree membertype2 = TREE_TYPE (TREE_TYPE (arg2));
+  tree basetype2 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg2));
+  if (!complete_type_or_else (basetype2, NULL_TREE))
+    return boolean_false_node;
+
+  if (!NON_UNION_CLASS_TYPE_P (basetype1)
+      || !NON_UNION_CLASS_TYPE_P (basetype2)
+      || !std_layout_type_p (basetype1)
+      || !std_layout_type_p (basetype2))
+    return boolean_false_node;
+
+  /* If the member types aren't layout compatible, then they
+     can't be corresponding members.  */
+  if (!layout_compatible_type_p (membertype1, membertype2))
+    return boolean_false_node;
+
+  if (TREE_CODE (arg1) == PTRMEM_CST)
+    arg1 = cplus_expand_constant (arg1);
+  if (TREE_CODE (arg2) == PTRMEM_CST)
+    arg2 = cplus_expand_constant (arg2);
+
+  if (null_member_pointer_value_p (arg1)
+      || null_member_pointer_value_p (arg2))
+    return boolean_false_node;
+
+  if (TREE_CODE (arg1) == INTEGER_CST
+      && TREE_CODE (arg2) == INTEGER_CST
+      && !tree_int_cst_equal (arg1, arg2))
+    return boolean_false_node;
+
+  if (TREE_CODE (arg2) == INTEGER_CST
+      && TREE_CODE (arg1) != INTEGER_CST)
+    {
+      std::swap (arg1, arg2);
+      std::swap (membertype1, membertype2);
+      std::swap (basetype1, basetype2);
+    }
+
+  tree ret = is_corresponding_member_aggr (loc, basetype1, membertype1, arg1,
+                                          basetype2, membertype2, arg2);
+  if (TREE_TYPE (ret) == boolean_type_node)
+    return ret;
+  gcc_assert (TREE_CODE (arg2) != INTEGER_CST);
+  if (TREE_CODE (arg1) == INTEGER_CST)
+    return fold_build2 (EQ_EXPR, boolean_type_node, arg1,
+                       fold_convert (TREE_TYPE (arg1), arg2));
+  ret = fold_build2 (LE_EXPR, boolean_type_node,
+                    fold_convert (pointer_sized_int_node, arg1),
+                    fold_convert (pointer_sized_int_node, ret));
+  return fold_build2 (TRUTH_AND_EXPR, boolean_type_node, ret,
+                     fold_build2 (EQ_EXPR, boolean_type_node, arg1,
+                                  fold_convert (TREE_TYPE (arg1), arg2)));
+}
+
 /* Actually evaluates the trait.  */
 
 static bool
@@ -10783,6 +11025,9 @@ trait_expr_value (cp_trait_kind kind, tr
     case CPTK_IS_FINAL:
       return CLASS_TYPE_P (type1) && CLASSTYPE_FINAL (type1);
 
+    case CPTK_IS_LAYOUT_COMPATIBLE:
+      return layout_compatible_type_p (type1, type2);
+
     case CPTK_IS_LITERAL_TYPE:
       return literal_type_p (type1);
 
@@ -10930,6 +11175,19 @@ finish_trait_expr (location_t loc, cp_tr
     case CPTK_IS_SAME_AS:
       break;
 
+    case CPTK_IS_LAYOUT_COMPATIBLE:
+      if (!array_of_unknown_bound_p (type1)
+         && TREE_CODE (type1) != VOID_TYPE
+         && !complete_type_or_else (type1, NULL_TREE))
+       /* We already issued an error.  */
+       return error_mark_node;
+      if (!array_of_unknown_bound_p (type2)
+         && TREE_CODE (type2) != VOID_TYPE
+         && !complete_type_or_else (type2, NULL_TREE))
+       /* We already issued an error.  */
+       return error_mark_node;
+      break;
+
     default:
       gcc_unreachable ();
     }
--- gcc/cp/typeck.c.jj  2021-08-12 22:36:44.775002298 +0200
+++ gcc/cp/typeck.c     2021-08-17 11:18:53.271850970 +0200
@@ -1621,6 +1621,164 @@ similar_type_p (tree type1, tree type2)
   return false;
 }
 
+/* Helper function for layout_compatible_type_p and
+   is_corresponding_member_aggr.  Advance to next members (NULL if
+   no further ones) and return true if those members are still part of
+   the common initial sequence.  */
+
+bool
+next_common_initial_seqence (tree &memb1, tree &memb2)
+{
+  while (memb1)
+    {
+      if (TREE_CODE (memb1) != FIELD_DECL
+         || (DECL_FIELD_IS_BASE (memb1) && is_empty_field (memb1)))
+       {
+         memb1 = DECL_CHAIN (memb1);
+         continue;
+       }
+      if (DECL_FIELD_IS_BASE (memb1))
+       {
+         memb1 = TYPE_FIELDS (TREE_TYPE (memb1));
+         continue;
+       }
+      break;
+    }
+  while (memb2)
+    {
+      if (TREE_CODE (memb2) != FIELD_DECL
+         || (DECL_FIELD_IS_BASE (memb2) && is_empty_field (memb2)))
+       {
+         memb2 = DECL_CHAIN (memb2);
+         continue;
+       }
+      if (DECL_FIELD_IS_BASE (memb2))
+       {
+         memb2 = TYPE_FIELDS (TREE_TYPE (memb2));
+         continue;
+       }
+      break;
+    }
+  if (memb1 == NULL_TREE && memb2 == NULL_TREE)
+    return true;
+  if (memb1 == NULL_TREE || memb2 == NULL_TREE)
+    return false;
+  if (DECL_BIT_FIELD_TYPE (memb1))
+    {
+      if (!DECL_BIT_FIELD_TYPE (memb2))
+       return false;
+      if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (memb1),
+                                    DECL_BIT_FIELD_TYPE (memb2)))
+       return false;
+      if (TYPE_PRECISION (TREE_TYPE (memb1))
+         != TYPE_PRECISION (TREE_TYPE (memb2)))
+       return false;
+    }
+  else if (DECL_BIT_FIELD_TYPE (memb2))
+    return false;
+  else if (!layout_compatible_type_p (TREE_TYPE (memb1), TREE_TYPE (memb2)))
+    return false;
+  if ((!lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (memb1)))
+      != !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (memb2)))
+    return false;
+  if (!tree_int_cst_equal (bit_position (memb1), bit_position (memb2)))
+    return false;
+  return true;
+}
+
+/* Return true if TYPE1 and TYPE2 are layout-compatible types.  */
+
+bool
+layout_compatible_type_p (tree type1, tree type2)
+{
+  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 (TREE_CODE (type1) == ENUMERAL_TYPE)
+    return (TYPE_ALIGN (type1) == TYPE_ALIGN (type2)
+           && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))
+           && 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)
+      && TYPE_ALIGN (type1) == TYPE_ALIGN (type2)
+      && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2)))
+    {
+      tree field1 = TYPE_FIELDS (type1);
+      tree field2 = TYPE_FIELDS (type2);
+      if (TREE_CODE (type1) == RECORD_TYPE)
+       {
+         while (1)
+           {
+             if (!next_common_initial_seqence (field1, field2))
+               return false;
+             if (field1 == NULL_TREE)
+               return true;
+             field1 = DECL_CHAIN (field1);
+             field2 = DECL_CHAIN (field2);
+           }
+       }
+      auto_vec<tree, 16> vec;
+      unsigned int count = 0;
+      for (; field1; field1 = DECL_CHAIN (field1))
+       if (TREE_CODE (field1) == FIELD_DECL)
+         count++;
+      for (; field2; field2 = DECL_CHAIN (field2))
+       if (TREE_CODE (field2) == FIELD_DECL)
+         vec.safe_push (field2);
+      if (count != vec.length ())
+       return false;
+      for (field1 = TYPE_FIELDS (type1); field1; field1 = DECL_CHAIN (field1))
+       {
+         if (TREE_CODE (field1) != FIELD_DECL)
+           continue;
+         unsigned int j;
+         tree t1 = DECL_BIT_FIELD_TYPE (field1);
+         if (t1 == NULL_TREE)
+           t1 = TREE_TYPE (field1);
+         FOR_EACH_VEC_ELT (vec, j, field2)
+           {
+             tree t2 = DECL_BIT_FIELD_TYPE (field2);
+             if (t2 == NULL_TREE)
+               t2 = TREE_TYPE (field2);
+             if (DECL_BIT_FIELD_TYPE (field1))
+               {
+                 if (!DECL_BIT_FIELD_TYPE (field2))
+                   continue;
+                 if (TYPE_PRECISION (TREE_TYPE (field1))
+                     != TYPE_PRECISION (TREE_TYPE (field2)))
+                   continue;
+               }
+             else if (DECL_BIT_FIELD_TYPE (field2))
+               continue;
+             if (!layout_compatible_type_p (t1, t2))
+               continue;
+             if ((!lookup_attribute ("no_unique_address",
+                                     DECL_ATTRIBUTES (field1)))
+                 != !lookup_attribute ("no_unique_address",
+                                       DECL_ATTRIBUTES (field2)))
+               continue;
+             break;
+           }
+         if (j == vec.length ())
+           return false;
+         vec.unordered_remove (j);
+       }
+      return true;
+    }
+
+  return same_type_p (type1, type2);
+}
+
 /* Returns 1 if TYPE1 is at least as qualified as TYPE2.  */
 
 bool
--- gcc/cp/cp-gimplify.c.jj     2021-08-12 22:36:44.678003631 +0200
+++ gcc/cp/cp-gimplify.c        2021-08-17 10:51:16.989464958 +0200
@@ -658,12 +658,20 @@ cp_gimplify_expr (tree *expr_p, gimple_s
                *expr_p
                  = fold_builtin_source_location (EXPR_LOCATION (*expr_p));
                break;
+             case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
+               *expr_p
+                 = fold_builtin_is_corresponding_member
+                       (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
+                        &CALL_EXPR_ARG (*expr_p, 0));
+               break;
              case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
                *expr_p
                  = fold_builtin_is_pointer_inverconvertible_with_class
                        (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
                         &CALL_EXPR_ARG (*expr_p, 0));
                break;
+             default:
+               break;
              }
        }
       break;
@@ -2579,6 +2587,11 @@ cp_fold (tree x)
              case CP_BUILT_IN_SOURCE_LOCATION:
                x = fold_builtin_source_location (EXPR_LOCATION (x));
                break;
+             case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
+               x = fold_builtin_is_corresponding_member
+                       (EXPR_LOCATION (x), call_expr_nargs (x),
+                        &CALL_EXPR_ARG (x, 0));
+               break;
              case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
                 x = fold_builtin_is_pointer_inverconvertible_with_class
                        (EXPR_LOCATION (x), call_expr_nargs (x),
--- gcc/cp/tree.c.jj    2021-08-12 22:36:44.730002916 +0200
+++ gcc/cp/tree.c       2021-08-17 10:51:16.989464958 +0200
@@ -455,6 +455,7 @@ builtin_valid_in_constant_expr_p (const_
          {
          case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
          case CP_BUILT_IN_SOURCE_LOCATION:
+         case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
          case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
            return true;
          default:
--- gcc/cp/cxx-pretty-print.c.jj        2021-08-12 22:36:44.681003589 +0200
+++ gcc/cp/cxx-pretty-print.c   2021-08-17 10:51:16.989464958 +0200
@@ -2645,6 +2645,9 @@ pp_cxx_trait_expression (cxx_pretty_prin
     case CPTK_IS_FINAL:
       pp_cxx_ws_string (pp, "__is_final");
       break;
+    case CPTK_IS_LAYOUT_COMPATIBLE:
+      pp_cxx_ws_string (pp, "__is_layout_compatible");
+      break;
     case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
       pp_cxx_ws_string (pp, "__is_pointer_interconvertible_base_of");
       break;
@@ -2700,6 +2703,7 @@ pp_cxx_trait_expression (cxx_pretty_prin
 
   if (kind == CPTK_IS_BASE_OF
       || kind == CPTK_IS_SAME_AS
+      || kind == CPTK_IS_LAYOUT_COMPATIBLE
       || kind == CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF)
     {
       pp_cxx_separate_with (pp, ',');
--- gcc/cp/class.c.jj   2021-08-12 22:36:44.626004345 +0200
+++ gcc/cp/class.c      2021-08-17 10:51:16.990464944 +0200
@@ -136,7 +136,6 @@ static bool check_field_decl (tree, tree
 static void check_field_decls (tree, tree *, int *, int *);
 static void build_base_fields (record_layout_info, splay_tree, tree *);
 static void check_methods (tree);
-static void remove_zero_width_bit_fields (tree);
 static bool accessible_nvdtor_p (tree);
 
 /* Used by find_flexarrays and related functions.  */
@@ -5754,31 +5753,6 @@ type_build_dtor_call (tree t)
   return false;
 }
 
-/* Remove all zero-width bit-fields from T.  */
-
-static void
-remove_zero_width_bit_fields (tree t)
-{
-  tree *fieldsp;
-
-  fieldsp = &TYPE_FIELDS (t);
-  while (*fieldsp)
-    {
-      if (TREE_CODE (*fieldsp) == FIELD_DECL
-         && DECL_C_BIT_FIELD (*fieldsp)
-         /* We should not be confused by the fact that grokbitfield
-            temporarily sets the width of the bit field into
-            DECL_BIT_FIELD_REPRESENTATIVE (*fieldsp).
-            check_bitfield_decl eventually sets DECL_SIZE (*fieldsp)
-            to that width.  */
-         && (DECL_SIZE (*fieldsp) == NULL_TREE
-             || integer_zerop (DECL_SIZE (*fieldsp))))
-       *fieldsp = DECL_CHAIN (*fieldsp);
-      else
-       fieldsp = &DECL_CHAIN (*fieldsp);
-    }
-}
-
 /* Returns TRUE iff we need a cookie when dynamically allocating an
    array whose elements have the indicated class TYPE.  */
 
@@ -6770,10 +6744,6 @@ layout_class_type (tree t, tree *virtual
       normalize_rli (rli);
     }
 
-  /* Delete all zero-width bit-fields from the list of fields.  Now
-     that the type is laid out they are no longer important.  */
-  remove_zero_width_bit_fields (t);
-
   if (CLASSTYPE_NON_LAYOUT_POD_P (t) || CLASSTYPE_EMPTY_P (t))
     {
       /* T needs a different layout as a base (eliding virtual bases
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C.jj    2021-08-17 
10:51:16.990464944 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C       2021-08-17 
10:51:16.990464944 +0200
@@ -0,0 +1,61 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+}
+
+struct A { int a; };
+struct B { const int b; };
+struct C { int a; unsigned int b; int f; A c; int : 0; int d; double e; };
+struct D { const int x; unsigned int y; int g; B z; int u; double w; };
+struct E { int a; [[no_unique_address]] int b; };
+struct F { int c; const int d; };
+struct G { double a; int b; double c; };
+struct H { const volatile double d; int e : 16; double f; };
+struct I { const double g; int h : 15; const double i; };
+struct J : public A {};
+struct K {};
+struct L : public K, public B {};
+union U { int a; };
+struct V { void foo () {}; };
+struct W { int a; private: int b; public: int c; };
+struct Z : public A, public B {};
+
+static_assert (std::is_corresponding_member (&A::a, &A::a));
+static_assert (std::is_corresponding_member (&A::a, &B::b));
+static_assert (std::is_corresponding_member (&C::a, &D::x));
+static_assert (std::is_corresponding_member (&C::b, &D::y));
+static_assert (std::is_corresponding_member (&C::f, &D::g));
+static_assert (std::is_corresponding_member (&C::c, &D::z));
+static_assert (!std::is_corresponding_member (&C::d, &D::u));
+static_assert (!std::is_corresponding_member (&C::e, &D::w));
+static_assert (!std::is_corresponding_member (&C::f, &D::x));
+static_assert (!std::is_corresponding_member (&C::a, &D::g));
+static_assert (std::is_corresponding_member (&E::a, &F::c));
+static_assert (!std::is_corresponding_member (&E::b, &F::d));
+static_assert (std::is_corresponding_member (&G::a, &H::d));
+static_assert (!std::is_corresponding_member (&G::c, &H::f));
+static_assert (std::is_corresponding_member (&H::d, &I::g));
+static_assert (!std::is_corresponding_member (&H::f, &I::i));
+static_assert (std::is_corresponding_member (&J::a, &B::b));
+static_assert (std::is_corresponding_member<J, B, int, const int> (&J::a, 
&B::b));
+static_assert (std::is_corresponding_member (&J::a, &L::b));
+static_assert (std::is_corresponding_member<J, L, int, const int> (&J::a, 
&L::b));
+static_assert (std::is_corresponding_member (&L::b, &B::b));
+static_assert (std::is_corresponding_member<L, B, const int, const int> 
(&L::b, &B::b));
+static_assert (!std::is_corresponding_member (&U::a, &U::a));
+static_assert (!std::is_corresponding_member (&A::a, (int A::*) nullptr));
+static_assert (!std::is_corresponding_member ((int A::*) nullptr, &A::a));
+static_assert (!std::is_corresponding_member ((int A::*) nullptr, (int A::*) 
nullptr));
+static_assert (!std::is_corresponding_member (&V::foo, &V::foo));
+static_assert (!std::is_corresponding_member (&W::a, &W::a));
+static_assert (!std::is_corresponding_member (&W::c, &W::c));
+static_assert (std::is_corresponding_member (&Z::a, &Z::b));
+static_assert (!std::is_corresponding_member<Z, Z, int, const int> (&Z::a, 
&Z::b));
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,158 @@
+// P0466R5
+// { dg-do run { target c++20 } }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+}
+
+struct A { int a; };
+struct B { const int b; };
+struct C { int a; unsigned int b; int f; A c; int : 0; int d; double e; };
+struct D { const int x; unsigned int y; int g; B z; int u; double w; };
+struct E { int a; [[no_unique_address]] int b; };
+struct F { int c; const int d; };
+struct G { double a; int b; double c; };
+struct H { const volatile double d; int e : 16; double f; };
+struct I { const double g; int h : 15; const double i; };
+struct J : public A {};
+struct K {};
+struct L : public K, public B {};
+union U { int a; };
+struct V { void foo () {}; };
+struct W { int a; private: int b; public: int c; };
+struct Z : public A, public B {};
+
+int
+main ()
+{
+  auto t1 = &A::a;
+  auto t2 = &A::a;
+  if (!std::is_corresponding_member (t1, t2))
+    __builtin_abort ();
+  auto t3 = &A::a;
+  auto t4 = &B::b;
+  if (!std::is_corresponding_member (t3, t4))
+    __builtin_abort ();
+  auto t5 = &C::a;
+  auto t6 = &D::x;
+  if (!std::is_corresponding_member (t5, t6))
+    __builtin_abort ();
+  auto t9 = &C::b;
+  auto t10 = &D::y;
+  if (!std::is_corresponding_member (t9, t10))
+    __builtin_abort ();
+  auto t11 = &C::f;
+  auto t12 = &D::g;
+  if (!std::is_corresponding_member (t11, t12))
+    __builtin_abort ();
+  auto t13 = &C::c;
+  auto t14 = &D::z;
+  if (!std::is_corresponding_member (t13, t14))
+    __builtin_abort ();
+  auto t15 = &C::d;
+  auto t16 = &D::u;
+  if (std::is_corresponding_member (t15, t16))
+    __builtin_abort ();
+  auto t17 = &C::e;
+  auto t18 = &D::w;
+  if (std::is_corresponding_member (t17, t18))
+    __builtin_abort ();
+  auto t19 = &C::f;
+  auto t20 = &D::x;
+  if (std::is_corresponding_member (t19, t20))
+    __builtin_abort ();
+  auto t21 = &C::a;
+  auto t22 = &D::g;
+  if (std::is_corresponding_member (t21, t22))
+    __builtin_abort ();
+  auto t23 = &E::a;
+  auto t24 = &F::c;
+  if (!std::is_corresponding_member (t23, t24))
+    __builtin_abort ();
+  auto t25 = &E::b;
+  auto t26 = &F::d;
+  if (std::is_corresponding_member (t25, t26))
+    __builtin_abort ();
+  auto t27 = &G::a;
+  auto t28 = &H::d;
+  if (!std::is_corresponding_member (t27, t28))
+    __builtin_abort ();
+  auto t29 = &G::c;
+  auto t30 = &H::f;
+  if (std::is_corresponding_member (t29, t30))
+    __builtin_abort ();
+  auto t31 = &H::d;
+  auto t32 = &I::g;
+  if (!std::is_corresponding_member (t31, t32))
+    __builtin_abort ();
+  auto t33 = &H::f;
+  auto t34 = &I::i;
+  if (std::is_corresponding_member (t33, t34))
+    __builtin_abort ();
+  auto t35 = &J::a;
+  auto t36 = &B::b;
+  if (!std::is_corresponding_member (t35, t36))
+    __builtin_abort ();
+  int J::*t37 = &J::a;
+  const int B::*t38 = &B::b;
+  if (!std::is_corresponding_member (t37, t38))
+    __builtin_abort ();
+  auto t39 = &J::a;
+  auto t40 = &L::b;
+  if (!std::is_corresponding_member (t39, t40))
+    __builtin_abort ();
+  int J::*t41 = &J::a;
+  const int L::*t42 = &L::b;
+  if (!std::is_corresponding_member (t41, t42))
+    __builtin_abort ();
+  auto t43 = &L::b;
+  auto t44 = &B::b;
+  if (!std::is_corresponding_member (t43, t44))
+    __builtin_abort ();
+  const int L::*t45 = &L::b;
+  const int B::*t46 = &B::b;
+  if (!std::is_corresponding_member (t45, t46))
+    __builtin_abort ();
+  auto t47 = &U::a;
+  auto t48 = &U::a;
+  if (std::is_corresponding_member (t47, t48))
+    __builtin_abort ();
+  auto t49 = &A::a;
+  auto t50 = (int A::*) nullptr;
+  if (std::is_corresponding_member (t49, t50))
+    __builtin_abort ();
+  auto t51 = (int A::*) nullptr;
+  auto t52 = &A::a;
+  if (std::is_corresponding_member (t51, t52))
+    __builtin_abort ();
+  auto t53 = (int A::*) nullptr;
+  auto t54 = (int A::*) nullptr;
+  if (std::is_corresponding_member (t53, t54))
+    __builtin_abort ();
+  auto t55 = &V::foo;
+  auto t56 = &V::foo;
+  if (std::is_corresponding_member (t55, t56))
+    __builtin_abort ();
+  auto t57 = &W::a;
+  auto t58 = &W::a;
+  if (std::is_corresponding_member (t57, t58))
+    __builtin_abort ();
+  auto t59 = &W::c;
+  auto t60 = &W::c;
+  if (std::is_corresponding_member (t59, t60))
+    __builtin_abort ();
+  auto t61 = &Z::a;
+  auto t62 = &Z::b;
+  if (!std::is_corresponding_member (t61, t62))
+    __builtin_abort ();
+  int Z::*t63 = &Z::a;
+  const int Z::*t64 = &Z::b;
+  if (std::is_corresponding_member (t63, t64))
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,14 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+struct A { int a; };
+struct B;
+
+bool a = __builtin_is_corresponding_member ();                 // { dg-error 
"needs two arguments" }
+bool b = __builtin_is_corresponding_member (&A::a);            // { dg-error 
"needs two arguments" }
+bool c = __builtin_is_corresponding_member (&A::a, &A::a, &A::a);      // { 
dg-error "needs two arguments" }
+bool d = __builtin_is_corresponding_member (&A::a, 1);                 // { 
dg-error "argument is not pointer to member" }
+bool e = __builtin_is_corresponding_member (1.0, &A::a);               // { 
dg-error "argument is not pointer to member" }
+bool f = __builtin_is_corresponding_member (1, A{});           // { dg-error 
"argument is not pointer to member" }
+bool g = __builtin_is_corresponding_member (&A::a, (int B::*) nullptr);        
// { dg-error "invalid use of incomplete type" }
+bool h = __builtin_is_corresponding_member ((int B::*) nullptr, &A::a);        
// { dg-error "invalid use of incomplete type" }
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,25 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);   // { dg-error "invalid 
use of incomplete type 'struct B'" }
+}
+}
+
+struct A { int a; };
+struct B;
+constexpr int B::*n = nullptr;
+constexpr auto a = std::is_corresponding_member (&A::a, n);    // { dg-error 
"invalid use of incomplete type 'struct B'" }
+constexpr auto b = std::is_corresponding_member (n, &A::a);    // { dg-error 
"invalid use of incomplete type 'struct B'" }
+
+void
+foo (int B::*m)
+{
+  std::is_corresponding_member (&A::a, m);
+  std::is_corresponding_member (m, &A::a);
+}
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,95 @@
+// P0466R5
+// { dg-do run { target c++20 } }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+}
+
+struct S {};
+struct T {};
+struct I { int a; };
+struct alignas(16) J { const int b; };
+struct K { char b; char s[15]; I c; short d; };
+struct L { char d; char t[15]; J e; short f; };
+struct U { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; 
[[no_unique_address]] S a3; short a4; };
+struct V { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; 
[[no_unique_address]] S b3; short b4; };
+struct U1 { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; 
[[no_unique_address]] S a3; short a4; };
+struct V1 { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; 
[[no_unique_address]] S b3; short b4; };
+struct A { int a; union { short b; long c; }; int d; signed char e; int f; };
+struct B { const int a; union { signed long b; short c; }; volatile int d; 
unsigned char e; int f; };
+struct A1 { int a; union { short b; long c; }; int d; short e; int f; };
+struct B1 { const int a; union { signed long b; short c; }; volatile int d; 
unsigned short e; int f; };
+
+static_assert (std::is_corresponding_member (&I::a, &J::b));
+static_assert (std::is_corresponding_member (&K::b, &L::d));
+static_assert (!std::is_corresponding_member (&K::c, &L::e));
+static_assert (std::is_corresponding_member (&U::a0, &V::b0));
+static_assert (!std::is_corresponding_member (&U::a4, &V::b4));
+static_assert (std::is_corresponding_member (&A::a, &B::a));
+static_assert (std::is_corresponding_member (&A::d, &B::d));
+static_assert (!std::is_corresponding_member (&A::e, &B::e));
+static_assert (!std::is_corresponding_member (&A::f, &B::f));
+static_assert (!std::is_corresponding_member (&A::a, &B::f));
+static_assert (!std::is_corresponding_member (&A::d, &B::a));
+static_assert (!std::is_corresponding_member (&A::a, &B::d));
+static_assert (!std::is_corresponding_member (&A::f, &B::a));
+static_assert (!std::is_corresponding_member (&A1::e, &B1::e));
+
+int
+main ()
+{
+  auto t1 = &I::a;
+  auto t2 = &J::b;
+  if (!std::is_corresponding_member (t1, t2))
+    __builtin_abort ();
+  auto t3 = &K::b;
+  auto t4 = &L::d;
+  if (!std::is_corresponding_member (t3, t4))
+    __builtin_abort ();
+  auto t5 = &K::c;
+  auto t6 = &L::e;
+  if (std::is_corresponding_member (t5, t6))
+    __builtin_abort ();
+  auto t7 = &U::a0;
+  auto t8 = &V::b0;
+  if (!std::is_corresponding_member (t7, t8))
+    __builtin_abort ();
+  auto t9 = &U::a4;
+  auto t10 = &V::b4;
+  if (std::is_corresponding_member (t9, t10))
+    __builtin_abort ();
+  auto t11 = &A::a;
+  auto t12 = &B::a;
+  auto t13 = &A::d;
+  auto t14 = &B::d;
+  auto t15 = &A::e;
+  auto t16 = &B::e;
+  auto t17 = &A::f;
+  auto t18 = &B::f;
+  if (!std::is_corresponding_member (t11, t12))
+    __builtin_abort ();
+  if (!std::is_corresponding_member (t13, t14))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t15, t16))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t17, t18))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t11, t18))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t13, t12))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t11, t14))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t17, t12))
+    __builtin_abort ();
+  auto t19 = &A1::e;
+  auto t20 = &B1::e;
+  if (std::is_corresponding_member (t19, t20))
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,34 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+// { dg-message "'__builtin_is_corresponding_member' not well defined for 
anonymous unions" "" { target *-*-* } .-2 }
+}
+
+struct S {};
+struct T {};
+struct I { int a; };
+struct alignas(16) J { const int b; };
+struct K { char b; char s[15]; alignas(16) I c; short d; };
+struct L { char d; char t[15]; J e; short f; };
+struct U { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; 
[[no_unique_address]] S a3; short a4; };
+struct V { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; 
[[no_unique_address]] S b3; short b4; };
+struct U1 { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; 
[[no_unique_address]] S a3; short a4; };
+struct V1 { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; 
[[no_unique_address]] S b3; short b4; };
+struct A { int a; union { short b; long c; }; int d; signed char e; int f; };
+struct B { const int a; union { signed long b; short c; }; volatile int d; 
unsigned char e; int f; };
+
+static_assert (!std::is_corresponding_member (&K::d, &L::f));
+static_assert (std::is_corresponding_member (&U::a1, &V::b1));
+static_assert (!std::is_corresponding_member (&U::a2, &V::b2));
+static_assert (!std::is_corresponding_member (&U::a3, &V::b3));
+static_assert (!std::is_corresponding_member (&U1::a3, &V1::b3));
+static_assert (!std::is_corresponding_member (&A::b, &B::c));
+constexpr auto a = std::is_corresponding_member (&A::c, &B::b);                
// { dg-message "required from here" }
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,71 @@
+// P0466R5
+// { dg-do run { target c++20 } }
+// { dg-options "" }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+}
+
+struct A { int a; struct { int b; short c; long d; }; int : 0; int e; };
+struct B { const signed int a; struct { int b; signed short c; signed long d; 
}; volatile int e; };
+struct C { int a; union { struct { short b; long c; }; long d; short e; }; 
signed int f; };
+struct D { int a; union { long b; short c; struct { short d; signed long e; }; 
}; int f; };
+
+static_assert (std::is_corresponding_member (&A::a, &B::a));
+static_assert (std::is_corresponding_member (&A::b, &B::b));
+static_assert (std::is_corresponding_member (&A::c, &B::c));
+static_assert (std::is_corresponding_member (&A::d, &B::d));
+static_assert (!std::is_corresponding_member (&A::e, &B::e));
+static_assert (!std::is_corresponding_member (&A::a, &B::b));
+static_assert (!std::is_corresponding_member (&A::b, &B::a));
+static_assert (std::is_corresponding_member (&C::a, &D::a));
+static_assert (std::is_corresponding_member (&C::f, &D::f));
+static_assert (!std::is_corresponding_member (&C::a, &D::f));
+static_assert (!std::is_corresponding_member (&C::f, &D::a));
+
+int
+main ()
+{
+  auto t1 = &A::a;
+  auto t2 = &B::a;
+  auto t3 = &A::b;
+  auto t4 = &B::b;
+  auto t5 = &A::c;
+  auto t6 = &B::c;
+  auto t7 = &A::d;
+  auto t8 = &B::d;
+  auto t9 = &A::e;
+  auto t10 = &B::e;
+  if (!std::is_corresponding_member (t1, t2))
+    __builtin_abort ();
+  if (!std::is_corresponding_member (t3, t4))
+    __builtin_abort ();
+  if (!std::is_corresponding_member (t5, t6))
+    __builtin_abort ();
+  if (!std::is_corresponding_member (t7, t8))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t9, t10))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t1, t4))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t3, t2))
+    __builtin_abort ();
+  auto t11 = &C::a;
+  auto t12 = &D::a;
+  auto t13 = &C::f;
+  auto t14 = &D::f;
+  if (!std::is_corresponding_member (t11, t12))
+    __builtin_abort ();
+  if (!std::is_corresponding_member (t13, t14))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t11, t14))
+    __builtin_abort ();
+  if (std::is_corresponding_member (t13, t12))
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C.jj    2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C       2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,25 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+namespace std
+{
+template <class S1, class S2, class M1, class M2>
+constexpr bool
+is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
+{
+  return __builtin_is_corresponding_member (m1, m2);
+}
+// { dg-message "'__builtin_is_corresponding_member' not well defined for 
anonymous unions" "" { target *-*-* } .-2 }
+}
+
+struct A { int a; struct { short b; short c; long d; }; int : 0; int e; };
+struct B { const signed int a; struct alignas(16) { short b; signed short c; 
signed long d; }; volatile int e; };
+struct C { int a; union { struct { int b; long c; }; long d; short e; }; 
signed int f; };
+struct D { int a; union { long b; short c; struct { int d; signed long e; }; 
}; int f; };
+
+static_assert (std::is_corresponding_member (&A::a, &B::a));
+static_assert (!std::is_corresponding_member (&A::b, &B::b));
+static_assert (!std::is_corresponding_member (&A::c, &B::c));
+static_assert (!std::is_corresponding_member (&A::d, &B::d));
+auto a = std::is_corresponding_member (&C::a, &D::a);          // { dg-message 
"required from here" }
--- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C.jj       2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C  2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,80 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <typename T, T v>
+struct integral_constant
+{
+  static constexpr T value = v;
+};
+
+template <typename, typename>
+struct is_layout_compatible;
+
+template<typename T, typename U>
+struct is_layout_compatible
+  : public integral_constant <bool, __is_layout_compatible (T, U)>
+{
+};
+
+template <typename T, typename U>
+inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
+}
+
+struct A { int a; char b; };
+struct B { const int c; volatile char d; };
+struct C { int a : 1; int : 7; int : 0; int b : 2; };
+struct D { int : 1; int c : 7; int : 0; int : 2; };
+struct E { int f : 1; int : 7; int g : 2; };
+struct F { int a; signed char b; };
+union G { int a; long long b; signed char c; unsigned char d; int e; };
+union H { long long f; unsigned char g; int h; int i; signed char j; };
+struct I : public A {};
+struct J {};
+struct K : public J {};
+struct L {};
+struct M : public K, L { const int a; volatile char b; };
+struct N {};
+struct O : public N, M {};
+struct P { int a; private: int b; public: int c; };
+struct Q { int a; private: int b; public: int c; };
+union U1 { int a; private: int b; public: int c; };
+union U2 { int a; private: int b; public: int c; };
+struct S {};
+struct T {};
+struct W;
+struct X;
+enum E1 : int { E11, E12 };
+enum E2 : int { E21, E22 };
+enum E3 : long { E31, E32 };
+enum E4 { E41, E42 };
+enum E5 { E51, E52 };
+
+static_assert (std::is_layout_compatible<int, const int>::value);
+static_assert (std::is_layout_compatible_v<double, volatile double>);
+static_assert (std::is_layout_compatible_v<A, B>);
+static_assert (std::is_layout_compatible_v<C, D>);
+static_assert (!std::is_layout_compatible_v<int, unsigned int>);
+static_assert (!std::is_layout_compatible_v<A, F>);
+static_assert (std::is_layout_compatible_v<G, H>);
+static_assert (std::is_layout_compatible_v<S, T>);
+static_assert (std::is_layout_compatible_v<A[3], A[3]>);
+static_assert (std::is_layout_compatible_v<A[], A[]>);
+static_assert (!std::is_layout_compatible_v<S[1], T[1]>);
+static_assert (std::is_layout_compatible_v<W[], W[]>);
+static_assert (!std::is_layout_compatible_v<W[], X[]>);
+static_assert (!std::is_layout_compatible_v<D, E>);
+static_assert (std::is_layout_compatible_v<void, const void>);
+static_assert (std::is_layout_compatible_v<I, const A>);
+static_assert (std::is_layout_compatible_v<volatile A, const I>);
+static_assert (std::is_layout_compatible_v<M, A>);
+static_assert (std::is_layout_compatible_v<O, M>);
+static_assert (std::is_layout_compatible_v<A, O>);
+static_assert (std::is_layout_compatible_v<P, P>);
+static_assert (!std::is_layout_compatible_v<P, Q>);
+static_assert (std::is_layout_compatible_v<U1, U1>);
+static_assert (!std::is_layout_compatible_v<U1, U2>);
+static_assert (std::is_layout_compatible_v<E1, E2>);
+static_assert (!std::is_layout_compatible_v<E1, E3>);
+static_assert (std::is_layout_compatible_v<E4, E5>);
--- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C.jj       2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C  2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,36 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <typename T, T v>
+struct integral_constant
+{
+  static constexpr T value = v;
+};
+
+template <typename, typename>
+struct is_layout_compatible;
+
+template<typename T, typename U>
+struct is_layout_compatible
+  : public integral_constant <bool, __is_layout_compatible (T, U)>
+{
+};
+
+template <typename T, typename U>
+inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
+}
+// { dg-error "invalid use of incomplete type 'struct W'" "" { target *-*-* } 
.-2 }
+// { dg-error "invalid use of incomplete type 'struct \[XY]'" "" { target 
*-*-* } .-3 }
+// { dg-error "invalid use of incomplete type 'struct Z'" "" { target *-*-* } 
.-4 }
+
+struct W;
+struct X;
+struct Y;
+struct Z;
+struct A {};
+
+auto a = std::is_layout_compatible_v<W, W>;
+auto b = std::is_layout_compatible_v<X, Y>;
+auto c = std::is_layout_compatible_v<A, Z>;
--- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C.jj       2021-08-17 
10:51:16.991464930 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C  2021-08-17 
10:51:16.991464930 +0200
@@ -0,0 +1,64 @@
+// P0466R5
+// { dg-do compile { target c++20 } }
+
+namespace std
+{
+template <typename T, T v>
+struct integral_constant
+{
+  static constexpr T value = v;
+};
+
+template <typename, typename>
+struct is_layout_compatible;
+
+template<typename T, typename U>
+struct is_layout_compatible
+  : public integral_constant <bool, __is_layout_compatible (T, U)>
+{
+};
+
+template <typename T, typename U>
+inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
+}
+
+// Weird cases.
+struct S {};
+struct T {};
+struct I { int a; };
+struct alignas(16) J { const int b; };
+struct K { I c; int d; };
+struct L { J e; int f; };
+union M { I u; };
+union N { J v; };
+union O { int a; int b; };
+union P { int a : 1; int b : 12; };
+enum Q : int { Q1, Q2 };
+enum alignas(16) R : int { R1, R2 };
+struct U { [[no_unique_address]] S a1; [[no_unique_address]] S a2; 
[[no_unique_address]] S a3; };
+struct V { [[no_unique_address]] S b1; [[no_unique_address]] T b2; 
[[no_unique_address]] S b3; };
+struct alignas(16) A : public I {};
+struct alignas(16) B {};
+struct C : public B, public I {};
+union D { int a : 3; int b : 9; };
+struct alignas(16) E { alignas(16) int a; alignas(16) int b; };
+struct alignas(16) F { int c; alignas(16) int d; };
+union alignas(16) G { int a; alignas(16) short b; };
+union alignas(16) H { short c; int d; };
+struct A1 { int a; };
+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<K, L>);
+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<E, F>);
+static_assert (std::is_layout_compatible_v<G, H>);
+static_assert (std::is_layout_compatible_v<C1, D1>);


        Jakub

Reply via email to