GCC Bugzilla – Attachment 51241 Details for
Bug 101539
[C++20] Implement builtins for layout-compatibility and pointer-interconvertibility traits
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
gcc12-pr101539.patch
gcc12-pr101539.patch (text/plain), 53.08 KB, created by
Jakub Jelinek
on 2021-08-02 14:40:23 UTC
(
hide
)
Description:
gcc12-pr101539.patch
Filename:
MIME Type:
Creator:
Jakub Jelinek
Created:
2021-08-02 14:40:23 UTC
Size:
53.08 KB
patch
obsolete
>2021-08-02 Jakub Jelinek <jakub@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, 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 (layout_compatible_type_p): New function. > * 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-07-31 18:35:23.879983218 +0200 >+++ gcc/c-family/c-common.h 2021-07-31 18:37:07.038600605 +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-07-31 09:17:09.190343988 +0200 >+++ gcc/c-family/c-common.c 2021-07-31 18:35:23.881983192 +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-07-31 09:17:09.219343584 +0200 >+++ gcc/cp/cp-tree.h 2021-07-31 18:45:26.871901455 +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, >@@ -6356,6 +6357,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 >@@ -7572,6 +7574,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); >@@ -7798,6 +7801,7 @@ 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 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-07-31 09:35:18.801178476 +0200 >+++ gcc/cp/parser.c 2021-07-31 18:35:23.888983098 +0200 >@@ -5798,6 +5798,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: >@@ -10686,6 +10687,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-07-31 09:17:09.217343612 +0200 >+++ gcc/cp/cp-objcp-common.c 2021-07-31 18:35:23.888983098 +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-07-31 09:17:09.217343612 +0200 >+++ gcc/cp/constraint.cc 2021-07-31 18:38:06.987797118 +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-07-31 18:30:02.310287105 +0200 >+++ gcc/cp/decl.c 2021-07-31 18:47:06.450566828 +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-07-31 09:17:09.217343612 +0200 >+++ gcc/cp/constexpr.c 2021-07-31 18:48:49.574184683 +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-07-31 09:17:09.229343445 +0200 >+++ gcc/cp/semantics.c 2021-08-02 15:42:04.735921585 +0200 >@@ -10670,6 +10670,308 @@ 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) >+ { >+ while (field1 && TREE_CODE (field1) != FIELD_DECL) >+ field1 = DECL_CHAIN (field1); >+ while (field2 && TREE_CODE (field2) != FIELD_DECL) >+ field2 = DECL_CHAIN (field2); >+ if (field1 && DECL_FIELD_IS_BASE (field1)) >+ { >+ if (is_empty_field (field1)) >+ { >+ field1 = DECL_CHAIN (field1); >+ continue; >+ } >+ return is_corresponding_member_aggr (loc, TREE_TYPE (field1), >+ membertype1, arg1, basetype2, >+ membertype2, arg2); >+ } >+ if (field2 && DECL_FIELD_IS_BASE (field2)) >+ { >+ if (is_empty_field (field2)) >+ { >+ field2 = DECL_CHAIN (field2); >+ continue; >+ } >+ return is_corresponding_member_aggr (loc, basetype1, membertype1, >+ arg1, TREE_TYPE (field2), >+ membertype2, arg2); >+ } >+ if (field1 == NULL_TREE || field2 == NULL_TREE) >+ break; >+ if ((!lookup_attribute ("no_unique_address", >+ DECL_ATTRIBUTES (field1))) >+ != !lookup_attribute ("no_unique_address", >+ DECL_ATTRIBUTES (field2))) >+ break; >+ if (DECL_BIT_FIELD_TYPE (field1)) >+ { >+ if (!DECL_BIT_FIELD_TYPE (field2)) >+ break; >+ if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (field1), >+ DECL_BIT_FIELD_TYPE (field2))) >+ break; >+ if (TYPE_PRECISION (TREE_TYPE (field1)) >+ != TYPE_PRECISION (TREE_TYPE (field2))) >+ break; >+ } >+ else if (DECL_BIT_FIELD_TYPE (field2)) >+ break; >+ else >+ { >+ if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field1), >+ membertype1) >+ && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field2), >+ membertype2)) >+ { >+ tree pos1 = byte_position (field1); >+ tree pos2 = byte_position (field2); >+ if (TREE_CODE (arg1) == INTEGER_CST >+ && TREE_CODE (arg2) == INTEGER_CST) >+ { >+ if (tree_int_cst_equal (arg1, pos1) >+ && tree_int_cst_equal (arg2, pos2)) >+ return boolean_true_node; >+ } >+ else if (TREE_CODE (arg1) == INTEGER_CST >+ && !tree_int_cst_equal (arg1, pos1)) >+ ; >+ else if (!tree_int_cst_equal (pos1, pos2)) >+ { >+ sorry_at (loc, "%<__builtin_is_corresponding_member%> " >+ "unsupported because corresponding members " >+ "%qD and %qD have different offsets", >+ field1, field2); >+ return boolean_false_node; >+ } >+ else if (TREE_CODE (arg1) == INTEGER_CST) >+ return pos2; >+ else >+ ret = pos1; >+ } >+ else if (ANON_AGGR_TYPE_P (TREE_TYPE (field1)) >+ && ANON_AGGR_TYPE_P (TREE_TYPE (field2))) >+ { >+ bool overlap = true; >+ tree pos1 = byte_position (field1); >+ tree pos2 = byte_position (field2); >+ 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, pos1) >+ || tree_int_cst_le (size_binop (PLUS_EXPR, pos1, 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, pos2) >+ || tree_int_cst_le (size_binop (PLUS_EXPR, pos2, 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), pos1); >+ tree narg2 = arg2; >+ if (TREE_CODE (arg2) == INTEGER_CST) >+ narg2 = size_binop (MINUS_EXPR, >+ fold_convert (sizetype, arg2), pos2); >+ 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_int_cst_equal (pos1, pos2)) >+ { >+ sorry_at (loc, >+ "%<__builtin_is_corresponding_member%> " >+ "unsupported because corresponding " >+ "members %qD and %qD have different " >+ "offsets", field1, field2); >+ return boolean_false_node; >+ } >+ if (TREE_CODE (arg1) == INTEGER_CST) >+ return size_binop (PLUS_EXPR, nret, pos2); >+ ret = size_binop (PLUS_EXPR, nret, pos1); >+ } >+ } >+ 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), pos1); >+ tree narg2 = arg2; >+ if (TREE_CODE (arg2) == INTEGER_CST) >+ narg2 = size_binop (MINUS_EXPR, >+ fold_convert (sizetype, arg2), pos2); >+ 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 (!layout_compatible_type_p (TREE_TYPE (field1), >+ TREE_TYPE (field2))) >+ 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 (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 >@@ -10760,6 +11062,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); > >@@ -10907,6 +11212,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-07-28 12:06:00.483928939 +0200 >+++ gcc/cp/typeck.c 2021-08-02 13:43:08.841809583 +0200 >@@ -1621,6 +1621,122 @@ similar_type_p (tree type1, tree type2) > return false; > } > >+/* 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 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)) >+ { >+ tree field1 = TYPE_FIELDS (type1); >+ tree field2 = TYPE_FIELDS (type2); >+ if (TREE_CODE (type1) == RECORD_TYPE) >+ { >+ while (1) >+ { >+ while (field1 && TREE_CODE (field1) != FIELD_DECL) >+ field1 = DECL_CHAIN (field1); >+ while (field2 && TREE_CODE (field2) != FIELD_DECL) >+ field2 = DECL_CHAIN (field2); >+ if (field1 && DECL_FIELD_IS_BASE (field1)) >+ { >+ if (is_empty_field (field1)) >+ { >+ field1 = DECL_CHAIN (field1); >+ continue; >+ } >+ return layout_compatible_type_p (TREE_TYPE (field1), type2); >+ } >+ if (field2 && DECL_FIELD_IS_BASE (field2)) >+ { >+ if (is_empty_field (field2)) >+ { >+ field2 = DECL_CHAIN (field2); >+ continue; >+ } >+ return layout_compatible_type_p (type1, TREE_TYPE (field2)); >+ } >+ if (field1 == NULL_TREE && field2 == NULL_TREE) >+ return true; >+ if (field1 == NULL_TREE || field2 == NULL_TREE) >+ return false; >+ if (DECL_BIT_FIELD_TYPE (field1)) >+ { >+ if (!DECL_BIT_FIELD_TYPE (field2)) >+ return false; >+ if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (field1), >+ DECL_BIT_FIELD_TYPE (field2))) >+ return false; >+ if (TYPE_PRECISION (TREE_TYPE (field1)) >+ != TYPE_PRECISION (TREE_TYPE (field2))) >+ return false; >+ } >+ else if (DECL_BIT_FIELD_TYPE (field2)) >+ return false; >+ else if (!layout_compatible_type_p (TREE_TYPE (field1), >+ TREE_TYPE (field2))) >+ return false; >+ if ((!lookup_attribute ("no_unique_address", >+ DECL_ATTRIBUTES (field1))) >+ != !lookup_attribute ("no_unique_address", >+ DECL_ATTRIBUTES (field2))) >+ return false; >+ 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 (layout_compatible_type_p (t1, t2)) >+ 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-07-31 09:17:09.217343612 +0200 >+++ gcc/cp/cp-gimplify.c 2021-07-31 18:50:32.293807956 +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-07-31 09:17:09.229343445 +0200 >+++ gcc/cp/tree.c 2021-07-31 18:51:04.221380038 +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-07-31 09:17:09.219343584 +0200 >+++ gcc/cp/cxx-pretty-print.c 2021-07-31 18:36:19.866232847 +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-07-31 09:17:09.216343626 +0200 >+++ gcc/cp/class.c 2021-07-31 18:35:24.313977398 +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-02 10:46:43.286747582 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C 2021-08-02 13:50:56.416259504 +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-02 13:44:01.104077456 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C 2021-08-02 13:59:17.547238521 +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-02 12:25:09.226579179 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C 2021-08-02 12:36:09.169289764 +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-02 14:28:20.806813256 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C 2021-08-02 15:48:17.521716240 +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-02 14:55:04.796357642 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C 2021-08-02 15:37:06.278092615 +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)); // Questionable >+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)) // Questionable >+ __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-02 14:57:41.326168300 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C 2021-08-02 15:47:38.961254670 +0200 >@@ -0,0 +1,40 @@ >+// 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' unsupported because corresponding members 'K::d' and 'L::f' have different offsets" "" { target *-*-* } .-2 } >+// { dg-message "'__builtin_is_corresponding_member' unsupported because corresponding members 'U::a2' and 'V::b2' have different offsets" "" { target *-*-* } .-3 } >+// { dg-message "'__builtin_is_corresponding_member' unsupported because corresponding members 'U::a3' and 'V::b3' have different offsets" "" { target *-*-* } .-4 } >+// { dg-message "'__builtin_is_corresponding_member' unsupported because corresponding members 'U1::a3' and 'V1::b3' have different offsets" "" { target *-*-* } .-5 } >+// { dg-message "'__builtin_is_corresponding_member' not well defined for anonymous unions" "" { target *-*-* } .-6 } >+} >+ >+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; }; >+ >+constexpr auto a = std::is_corresponding_member (&K::d, &L::f); // { dg-message "required from here" } >+constexpr auto b = std::is_corresponding_member (&U::a1, &V::b1); // { dg-message "required from here" } >+constexpr auto c = std::is_corresponding_member (&U::a2, &V::b2); // { dg-message "required from here" } >+constexpr auto d = std::is_corresponding_member (&U::a3, &V::b3); // No "required from here" here, as constexpr evaluation >+ // doesn't sorry and the template function is already >+ // instantiated >+constexpr auto e = std::is_corresponding_member (&U1::a3, &V1::b3); // { dg-message "required from here" } >+constexpr auto f = std::is_corresponding_member (&A::b, &B::c); // { dg-message "required from here" } >+constexpr auto g = 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-02 15:51:49.720753228 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C 2021-08-02 16:04:18.152328820 +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-02 15:57:25.464067775 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C 2021-08-02 16:12:24.067571542 +0200 >@@ -0,0 +1,26 @@ >+// 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' unsupported because corresponding members" "" { target *-*-* } .-2 } >+// { dg-message "'__builtin_is_corresponding_member' not well defined for anonymous unions" "" { target *-*-* } .-3 } >+} >+ >+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)); >+auto a = std::is_corresponding_member (&A::b, &B::b); // { dg-message "required from here" } >+auto b = std::is_corresponding_member (&A::c, &B::c); >+auto c = std::is_corresponding_member (&A::d, &B::d); // { dg-message "required from here" } >+auto d = std::is_corresponding_member (&C::a, &D::a); // { dg-message "required from here" } >--- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C.jj 2021-07-31 18:35:24.313977398 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C 2021-08-02 13:37:19.860699332 +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-07-31 19:17:23.446257425 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C 2021-07-31 19:29:54.458235589 +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-02 13:05:11.804773825 +0200 >+++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C 2021-08-02 13:39:24.156957116 +0200 >@@ -0,0 +1,51 @@ >+// 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); >+} >+ >+// Unexpected and 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 {}; >+ >+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<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>);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 101539
:
51186
|
51192
|
51219
| 51241