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