diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 00ac3c5278b..017e41537ac 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -420,6 +420,7 @@ const struct c_common_resword c_common_reswords[] = { "__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 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 025123a3c91..d66bf158d1e 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -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, diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 6f31700c06c..7138e304937 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -136,7 +136,6 @@ static bool check_field_decl (tree, tree, int *, int *); 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 *virtuals_p) 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 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 25d84a377d8..b9c006217be 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1438,6 +1438,18 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, = 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); diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index e608c5aed1b..1aaf1e27886 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3628,6 +3628,9 @@ diagnose_trait_expr (tree expr, tree args) 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; diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 6e274ac62af..bf928a82ce9 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -658,12 +658,20 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) *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), diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index beef0123b04..98fd96227c4 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -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: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bd3f12a393e..14e2db26f77 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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"))) tinst_level { 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 (tree); 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_tree, const_tree, int); 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); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index b89916206a6..25cabfee39f 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -2645,6 +2645,9 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t) 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_printer *pp, tree t) 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, ','); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index b3671ee8956..32d07babf43 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4469,6 +4469,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, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c31965a6d49..9de72f8db51 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -5816,6 +5816,7 @@ cp_parser_primary_expression (cp_parser *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, enum rid keyword) 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; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 0198d2d8e8b..e191aa36c98 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -10693,6 +10693,258 @@ fold_builtin_is_pointer_inverconvertible_with_class (location_t loc, int nargs, 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; + /* If both arg1 and arg2 are INTEGER_CSTs, is_corresponding_member_aggr + already returns boolean_{true,false}_node whether those particular + members are corresponding members or not. Otherwise, if only + one of them is INTEGER_CST (canonicalized to first being INTEGER_CST + above), it returns boolean_false_node if it is certainly not a + corresponding member and otherwise we need to do a runtime check that + those two OFFSET_TYPE offsets are equal. + If neither of the operands is INTEGER_CST, is_corresponding_member_aggr + returns the largest offset at which the members would be corresponding + members, so perform arg1 <= ret && arg1 == arg2 runtime check. */ + 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 +11035,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) 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 +11185,19 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) 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 (); } diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index e8831b21802..3c62dd74380 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -455,6 +455,7 @@ builtin_valid_in_constant_expr_p (const_tree decl) { 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: diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 738e69a0440..a46c6d2340d 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -1621,6 +1621,176 @@ 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); + } + } + /* Otherwise both types must be union types. + The standard says: + "Two standard-layout unions are layout-compatible if they have + the same number of non-static data members and corresponding + non-static data members (in any order) have layout-compatible + types." + but the code anticipates that bitfield vs. non-bitfield, + different bitfield widths or presence/absence of + [[no_unique_address]] should be checked as well. */ + auto_vec 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); + /* Discussions on core lean towards treating multiple union fields + of the same type as the same field, so this might need changing + in the future. */ + if (count != vec.length ()) + return false; + 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 diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C new file mode 100644 index 00000000000..dd14c44e73d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C @@ -0,0 +1,61 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +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::a, &B::b)); +static_assert (std::is_corresponding_member (&J::a, &L::b)); +static_assert (std::is_corresponding_member (&J::a, &L::b)); +static_assert (std::is_corresponding_member (&L::b, &B::b)); +static_assert (std::is_corresponding_member (&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::a, &Z::b)); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C new file mode 100644 index 00000000000..1cedbcb7dc4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C @@ -0,0 +1,158 @@ +// P0466R5 +// { dg-do run { target c++20 } } + +namespace std +{ +template +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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C new file mode 100644 index 00000000000..1ff510c8146 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C new file mode 100644 index 00000000000..6b74090306b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C @@ -0,0 +1,25 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +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); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C new file mode 100644 index 00000000000..b95630959b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C @@ -0,0 +1,95 @@ +// P0466R5 +// { dg-do run { target c++20 } } + +namespace std +{ +template +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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C new file mode 100644 index 00000000000..e4f53bbe5ca --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C @@ -0,0 +1,34 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C new file mode 100644 index 00000000000..602ca012775 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C @@ -0,0 +1,71 @@ +// P0466R5 +// { dg-do run { target c++20 } } +// { dg-options "" } + +namespace std +{ +template +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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C new file mode 100644 index 00000000000..1a33908627d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C @@ -0,0 +1,25 @@ +// P0466R5 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +namespace std +{ +template +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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C new file mode 100644 index 00000000000..dbc2a9a93eb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C @@ -0,0 +1,80 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +struct integral_constant +{ + static constexpr T value = v; +}; + +template +struct is_layout_compatible; + +template +struct is_layout_compatible + : public integral_constant +{ +}; + +template +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::value); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C new file mode 100644 index 00000000000..bf902f385a5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C @@ -0,0 +1,36 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +struct integral_constant +{ + static constexpr T value = v; +}; + +template +struct is_layout_compatible; + +template +struct is_layout_compatible + : public integral_constant +{ +}; + +template +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; +auto b = std::is_layout_compatible_v; +auto c = std::is_layout_compatible_v; diff --git a/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C new file mode 100644 index 00000000000..c5485874660 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C @@ -0,0 +1,64 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template +struct integral_constant +{ + static constexpr T value = v; +}; + +template +struct is_layout_compatible; + +template +struct is_layout_compatible + : public integral_constant +{ +}; + +template +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); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (!std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v); +static_assert (std::is_layout_compatible_v);