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