On Thu, Jul 29, 2021 at 04:38:44PM -0400, Jason Merrill wrote: > > +/* Helper function for pointer_interconvertible_base_of_p. Verify > > + that BINFO_TYPE (BINFO) is pointer interconvertible with BASE. */ > > + > > +static bool > > +pointer_interconvertible_base_of_p_1 (tree binfo, tree base) > > +{ > > + for (tree field = TYPE_FIELDS (BINFO_TYPE (binfo)); > > + field; field = DECL_CHAIN (field)) > > + if (TREE_CODE (field) == FIELD_DECL && !DECL_FIELD_IS_BASE (field)) > > + return false; > > I think checking non-static data members is a bug in the resolution of CWG > 2254, which correctly changed 11.4 to say that the address of a > standard-layout class is the same as the address of each base whether or not > the class has non-static data members, but didn't change > pointer-interconvertibility enough to match. I've raised this with CWG. > > I think we don't need this function at all.
Ok. ... > Instead of checking !UNION_TYPE twice above, you could check it once here > and return false. Here is an updated patch, which includes the incremental patch for non-std-layout unions (with no changes for non-stdlayout in anon structure in union though) and has your review comments above incorporated. All the changes from the combination of the original and incremental patch are in gcc/cp/semantics.c and gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C. 2021-07-30 Jakub Jelinek <ja...@redhat.com> PR c++/101539 gcc/c-family/ * c-common.h (enum rid): Add RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF. * c-common.c (c_common_reswords): Add __is_pointer_interconvertible_base_of. gcc/cp/ * cp-tree.h (enum cp_trait_kind): Add CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (enum cp_built_in_function): Add CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. (fold_builtin_is_pointer_inverconvertible_with_class): Declare. * parser.c (cp_parser_primary_expression): Handle RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (cp_parser_trait_expr): Likewise. * cp-objcp-common.c (names_builtin_p): Likewise. * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. * decl.c (cxx_init_decl_processing): Register __builtin_is_pointer_interconvertible_with_class builtin. * constexpr.c (cxx_eval_builtin_function_call): Handle CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS builtin. * semantics.c (pointer_interconvertible_base_of_p, first_nonstatic_data_member_p, fold_builtin_is_pointer_inverconvertible_with_class): New functions. (trait_expr_value): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (finish_trait_expr): Likewise. Formatting fix. * cp-gimplify.c (cp_gimplify_expr): Fold CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. Call fndecl_built_in_p just once. (cp_fold): Likewise. * tree.c (builtin_valid_in_constant_expr_p): Handle CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. Call fndecl_built_in_p just once. * cxx-pretty-print.c (pp_cxx_trait_expression): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. gcc/testsuite/ * g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C: New test. --- gcc/c-family/c-common.h.jj 2021-07-29 13:24:08.368481637 +0200 +++ gcc/c-family/c-common.h 2021-07-30 11:19:39.391615252 +0200 @@ -174,6 +174,7 @@ enum rid RID_IS_BASE_OF, RID_IS_CLASS, RID_IS_EMPTY, RID_IS_ENUM, RID_IS_FINAL, RID_IS_LITERAL_TYPE, + RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, RID_IS_POD, RID_IS_POLYMORPHIC, RID_IS_SAME_AS, RID_IS_STD_LAYOUT, RID_IS_TRIVIAL, --- gcc/c-family/c-common.c.jj 2021-07-29 13:24:42.667013302 +0200 +++ gcc/c-family/c-common.c 2021-07-30 11:19:39.411614978 +0200 @@ -421,6 +421,8 @@ const struct c_common_resword c_common_r { "__is_enum", RID_IS_ENUM, D_CXXONLY }, { "__is_final", RID_IS_FINAL, D_CXXONLY }, { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY }, + { "__is_pointer_interconvertible_base_of", + RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY }, { "__is_pod", RID_IS_POD, D_CXXONLY }, { "__is_polymorphic", RID_IS_POLYMORPHIC, D_CXXONLY }, { "__is_same", RID_IS_SAME_AS, D_CXXONLY }, --- gcc/cp/cp-tree.h.jj 2021-07-29 13:24:08.673477473 +0200 +++ gcc/cp/cp-tree.h 2021-07-30 11:19:39.426614772 +0200 @@ -1366,6 +1366,7 @@ enum cp_trait_kind CPTK_IS_ENUM, CPTK_IS_FINAL, CPTK_IS_LITERAL_TYPE, + CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF, CPTK_IS_POD, CPTK_IS_POLYMORPHIC, CPTK_IS_SAME_AS, @@ -6355,6 +6356,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_POINTER_INTERCONVERTIBLE_WITH_CLASS, CP_BUILT_IN_SOURCE_LOCATION, CP_BUILT_IN_LAST }; @@ -7570,6 +7572,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_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); extern tree build_lambda_object (tree); --- gcc/cp/parser.c.jj 2021-07-30 10:30:07.115413053 +0200 +++ gcc/cp/parser.c 2021-07-30 11:19:39.431614703 +0200 @@ -5799,6 +5799,7 @@ cp_parser_primary_expression (cp_parser case RID_IS_ENUM: case RID_IS_FINAL: case RID_IS_LITERAL_TYPE: + case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF: case RID_IS_POD: case RID_IS_POLYMORPHIC: case RID_IS_SAME_AS: @@ -10688,6 +10689,10 @@ cp_parser_trait_expr (cp_parser* parser, case RID_IS_LITERAL_TYPE: kind = CPTK_IS_LITERAL_TYPE; break; + case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF: + kind = CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF; + binary = true; + break; case RID_IS_POD: kind = CPTK_IS_POD; break; --- gcc/cp/cp-objcp-common.c.jj 2021-07-29 13:24:08.599478483 +0200 +++ gcc/cp/cp-objcp-common.c 2021-07-30 11:19:39.440614580 +0200 @@ -414,6 +414,7 @@ names_builtin_p (const char *name) case RID_IS_ENUM: case RID_IS_FINAL: case RID_IS_LITERAL_TYPE: + case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF: case RID_IS_POD: case RID_IS_POLYMORPHIC: case RID_IS_SAME_AS: --- gcc/cp/constraint.cc.jj 2021-07-29 13:24:08.527479466 +0200 +++ gcc/cp/constraint.cc 2021-07-30 11:19:39.455614374 +0200 @@ -3631,6 +3631,10 @@ diagnose_trait_expr (tree expr, tree arg case CPTK_IS_LITERAL_TYPE: inform (loc, " %qT is not a literal type", t1); break; + case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF: + inform (loc, " %qT is not pointer-interconvertible base of %qT", + t1, t2); + break; case CPTK_IS_POD: inform (loc, " %qT is not a POD type", t1); break; --- gcc/cp/decl.c.jj 2021-07-29 13:24:08.797475780 +0200 +++ gcc/cp/decl.c 2021-07-30 11:19:39.476614087 +0200 @@ -4467,6 +4467,15 @@ cxx_init_decl_processing (void) BUILT_IN_FRONTEND, NULL, NULL_TREE); set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF); + tree bool_vaftype = build_varargs_function_type_list (boolean_type_node, + NULL_TREE); + decl + = add_builtin_function ("__builtin_is_pointer_interconvertible_with_class", + bool_vaftype, + CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS, + BUILT_IN_FRONTEND, NULL, NULL_TREE); + set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF); + integer_two_node = build_int_cst (NULL_TREE, 2); /* Guess at the initial static decls size. */ --- gcc/cp/constexpr.c.jj 2021-07-29 13:24:08.447480558 +0200 +++ gcc/cp/constexpr.c 2021-07-30 11:19:39.493613854 +0200 @@ -1427,8 +1427,20 @@ cxx_eval_builtin_function_call (const co && ctx->call && ctx->call->fundef) current_function_decl = ctx->call->fundef->decl; - new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t), - CALL_EXPR_FN (t), nargs, args); + if (fndecl_built_in_p (fun, + CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS, + BUILT_IN_FRONTEND)) + { + location_t loc = EXPR_LOCATION (t); + if (nargs >= 1) + VERIFY_CONSTANT (args[0]); + new_call + = fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs, + args); + } + else + new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t), + CALL_EXPR_FN (t), nargs, args); current_function_decl = save_cur_fn; force_folding_builtin_constant_p = save_ffbcp; if (new_call == NULL) --- gcc/cp/semantics.c.jj 2021-07-29 13:24:08.960473554 +0200 +++ gcc/cp/semantics.c 2021-07-30 11:27:16.247352685 +0200 @@ -10566,6 +10566,111 @@ classtype_has_nothrow_assign_or_copy_p ( return saw_copy; } +/* Return true if DERIVED is pointer interconvertible base of BASE. */ + +static bool +pointer_interconvertible_base_of_p (tree base, tree derived) +{ + if (base == error_mark_node || derived == error_mark_node) + return false; + base = TYPE_MAIN_VARIANT (base); + derived = TYPE_MAIN_VARIANT (derived); + if (!NON_UNION_CLASS_TYPE_P (base) + || !NON_UNION_CLASS_TYPE_P (derived)) + return false; + + if (same_type_p (base, derived)) + return true; + + if (!std_layout_type_p (derived)) + return false; + + return uniquely_derived_from_p (base, derived); +} + +/* Helper function for fold_builtin_is_pointer_inverconvertible_with_class, + return true if MEMBERTYPE is the type of the first non-static data member + of TYPE or for unions of any members. */ +static bool +first_nonstatic_data_member_p (tree type, tree membertype) +{ + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + if (DECL_FIELD_IS_BASE (field) && is_empty_field (field)) + continue; + if (DECL_FIELD_IS_BASE (field)) + return first_nonstatic_data_member_p (TREE_TYPE (field), membertype); + if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) + { + if ((TREE_CODE (type) != UNION_TYPE + || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE + || std_layout_type_p (TREE_TYPE (field))) + && first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) + return true; + } + else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), + membertype)) + return true; + if (TREE_CODE (type) != UNION_TYPE) + return false; + } + return false; +} + +/* Fold __builtin_is_pointer_interconvertible_with_class call. */ + +tree +fold_builtin_is_pointer_inverconvertible_with_class (location_t loc, int nargs, + tree *args) +{ + /* Unless users call the builtin directly, the following 3 checks should be + ensured from std::is_pointer_interconvertible_with_class function + template. */ + if (nargs != 1) + { + error_at (loc, "%<__builtin_is_pointer_interconvertible_with_class%> " + "needs a single argument"); + return boolean_false_node; + } + tree arg = args[0]; + if (error_operand_p (arg)) + return boolean_false_node; + if (!TYPE_PTRMEM_P (TREE_TYPE (arg))) + { + error_at (loc, "%<__builtin_is_pointer_interconvertible_with_class%> " + "argument is not pointer to member"); + return boolean_false_node; + } + + if (!TYPE_PTRDATAMEM_P (TREE_TYPE (arg))) + return boolean_false_node; + + tree membertype = TREE_TYPE (TREE_TYPE (arg)); + tree basetype = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg)); + if (!complete_type_or_else (basetype, NULL_TREE)) + return boolean_false_node; + + if (TREE_CODE (basetype) != UNION_TYPE + && !std_layout_type_p (basetype)) + return boolean_false_node; + + if (!first_nonstatic_data_member_p (basetype, membertype)) + return boolean_false_node; + + if (TREE_CODE (arg) == PTRMEM_CST) + arg = cplus_expand_constant (arg); + + if (integer_nonzerop (arg)) + return boolean_false_node; + if (integer_zerop (arg)) + return boolean_true_node; + + return fold_build2 (EQ_EXPR, boolean_type_node, arg, + build_zero_cst (TREE_TYPE (arg))); +} + /* Actually evaluates the trait. */ static bool @@ -10659,6 +10764,9 @@ trait_expr_value (cp_trait_kind kind, tr case CPTK_IS_LITERAL_TYPE: return literal_type_p (type1); + case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF: + return pointer_interconvertible_base_of_p (type1, type2); + case CPTK_IS_POD: return pod_type_p (type1); @@ -10786,6 +10894,7 @@ finish_trait_expr (location_t loc, cp_tr break; case CPTK_IS_BASE_OF: + case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF: if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2) && !same_type_ignoring_top_level_qualifiers_p (type1, type2) && !complete_type_or_else (type2, NULL_TREE)) @@ -10803,9 +10912,9 @@ finish_trait_expr (location_t loc, cp_tr gcc_unreachable (); } -tree val = (trait_expr_value (kind, type1, type2) - ? boolean_true_node : boolean_false_node); - return maybe_wrap_with_location (val, loc); + tree val = (trait_expr_value (kind, type1, type2) + ? boolean_true_node : boolean_false_node); + return maybe_wrap_with_location (val, loc); } /* Do-nothing variants of functions to handle pragma FLOAT_CONST_DECIMAL64, --- gcc/cp/cp-gimplify.c.jj 2021-07-29 13:24:08.599478483 +0200 +++ gcc/cp/cp-gimplify.c 2021-07-30 11:19:39.525613415 +0200 @@ -648,14 +648,23 @@ cp_gimplify_expr (tree *expr_p, gimple_s if (ret != GS_ERROR) { tree decl = cp_get_callee_fndecl_nofold (*expr_p); - if (decl - && fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND)) - *expr_p = boolean_false_node; - else if (decl - && fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION, - BUILT_IN_FRONTEND)) - *expr_p = fold_builtin_source_location (EXPR_LOCATION (*expr_p)); + if (decl && fndecl_built_in_p (decl, BUILT_IN_FRONTEND)) + switch (DECL_FE_FUNCTION_CODE (decl)) + { + case CP_BUILT_IN_IS_CONSTANT_EVALUATED: + *expr_p = boolean_false_node; + break; + case CP_BUILT_IN_SOURCE_LOCATION: + *expr_p + = fold_builtin_source_location (EXPR_LOCATION (*expr_p)); + 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; + } } break; @@ -2560,11 +2569,26 @@ cp_fold (tree x) && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) nw = 1; - /* Defer folding __builtin_is_constant_evaluated. */ - if (callee - && fndecl_built_in_p (callee, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND)) - break; + if (callee && fndecl_built_in_p (callee, BUILT_IN_FRONTEND)) + { + switch (DECL_FE_FUNCTION_CODE (callee)) + { + /* Defer folding __builtin_is_constant_evaluated. */ + case CP_BUILT_IN_IS_CONSTANT_EVALUATED: + break; + case CP_BUILT_IN_SOURCE_LOCATION: + x = fold_builtin_source_location (EXPR_LOCATION (x)); + 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), + &CALL_EXPR_ARG (x, 0)); + break; + default: + break; + } + break; + } if (callee && fndecl_built_in_p (callee, CP_BUILT_IN_SOURCE_LOCATION, --- gcc/cp/tree.c.jj 2021-07-29 13:24:09.017472775 +0200 +++ gcc/cp/tree.c 2021-07-30 11:19:39.539613223 +0200 @@ -450,11 +450,16 @@ builtin_valid_in_constant_expr_p (const_ return false; if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL) { - if (fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND) - || fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION, - BUILT_IN_FRONTEND)) - return true; + if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND)) + switch (DECL_FE_FUNCTION_CODE (decl)) + { + case CP_BUILT_IN_IS_CONSTANT_EVALUATED: + case CP_BUILT_IN_SOURCE_LOCATION: + case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS: + return true; + default: + break; + } /* Not a built-in. */ return false; } --- gcc/cp/cxx-pretty-print.c.jj 2021-07-29 13:24:08.734476640 +0200 +++ gcc/cp/cxx-pretty-print.c 2021-07-30 11:19:39.553613031 +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_POINTER_INTERCONVERTIBLE_BASE_OF: + pp_cxx_ws_string (pp, "__is_pointer_interconvertible_base_of"); + break; case CPTK_IS_POD: pp_cxx_ws_string (pp, "__is_pod"); break; @@ -2695,7 +2698,9 @@ pp_cxx_trait_expression (cxx_pretty_prin pp_cxx_left_paren (pp); pp->type_id (TRAIT_EXPR_TYPE1 (t)); - if (kind == CPTK_IS_BASE_OF || kind == CPTK_IS_SAME_AS) + if (kind == CPTK_IS_BASE_OF + || kind == CPTK_IS_SAME_AS + || kind == CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF) { pp_cxx_separate_with (pp, ','); pp->type_id (TRAIT_EXPR_TYPE2 (t)); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C 2021-07-30 11:39:51.666998275 +0200 @@ -0,0 +1,55 @@ +// 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_pointer_interconvertible_base_of; + +template<typename T, typename U> +struct is_pointer_interconvertible_base_of + : public integral_constant <bool, __is_pointer_interconvertible_base_of (T, U)> +{ +}; + +template <typename T, typename U> +inline constexpr bool is_pointer_interconvertible_base_of_v = __is_pointer_interconvertible_base_of (T, U); +} + +struct A; +struct B { int b; }; +struct C : virtual B { int c; }; +struct D {}; +struct E {}; +struct F : public B, D, E {}; +struct G : public D, E { int g; }; +struct H {}; +struct I : public G, H {}; +struct J { int j1; private: int j2; }; +struct K : public J {}; +union U { int a; }; + +static_assert (std::is_pointer_interconvertible_base_of<A, A>::value); +static_assert (std::is_pointer_interconvertible_base_of_v<A, A>); +static_assert (std::is_pointer_interconvertible_base_of_v<const A, volatile A>); +static_assert (std::is_pointer_interconvertible_base_of_v<B, const B>); +static_assert (std::is_pointer_interconvertible_base_of_v<C, const volatile C>); +static_assert (!std::is_pointer_interconvertible_base_of_v<D, E>); +static_assert (!std::is_pointer_interconvertible_base_of_v<D, const B>); +static_assert (std::is_pointer_interconvertible_base_of_v<const B, F>); +static_assert (std::is_pointer_interconvertible_base_of_v<D, const F>); +static_assert (std::is_pointer_interconvertible_base_of_v<E, F>); +static_assert (std::is_pointer_interconvertible_base_of_v<D, volatile G>); +static_assert (std::is_pointer_interconvertible_base_of_v<const E, volatile G>); +static_assert (std::is_pointer_interconvertible_base_of_v<D, I>); +static_assert (std::is_pointer_interconvertible_base_of_v<const E, const I>); +static_assert (std::is_pointer_interconvertible_base_of_v<G, I>); +static_assert (std::is_pointer_interconvertible_base_of_v<H, volatile I>); +static_assert (!std::is_pointer_interconvertible_base_of_v<volatile J, const K>); +static_assert (!std::is_pointer_interconvertible_base_of_v<U, U>); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-30 11:19:52.473435926 +0200 @@ -0,0 +1,65 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template <class S, class M> +constexpr bool +is_pointer_interconvertible_with_class (M S::*m) noexcept +{ + return __builtin_is_pointer_interconvertible_with_class (m); +} +} + +struct A; +struct B { int b; double b2; }; +struct C : virtual B { int c; }; +struct D {}; +struct E {}; +struct F : public B, D, E {}; +struct G : public D, E { int g; }; +struct H {}; +struct I : public G, H {}; +struct J { int j1; private: int j2; public: int j3; }; +struct K : public J {}; +struct L : public B, D, E {}; +struct M { D d [[no_unique_address]]; E e [[no_unique_address]]; int f; }; +union U { int a; double b; long long c; }; +struct V { union { int a; long b; }; int c; }; +union X { int a; union { short b; long c; }; long long d; }; +struct Y { void foo () {} }; +union Z { int a; private: int b; protected: int c; public: int d; }; + +static_assert (std::is_pointer_interconvertible_with_class (&B::b)); +static_assert (!std::is_pointer_interconvertible_with_class (&B::b2)); +static_assert (std::is_pointer_interconvertible_with_class (&C::b)); +static_assert (std::is_pointer_interconvertible_with_class (&F::b)); +static_assert (std::is_pointer_interconvertible_with_class<F, int> (&F::b)); +static_assert (std::is_pointer_interconvertible_with_class (&G::g)); +static_assert (std::is_pointer_interconvertible_with_class<G, int> (&G::g)); +static_assert (std::is_pointer_interconvertible_with_class (&I::g)); +static_assert (std::is_pointer_interconvertible_with_class<I, int> (&I::g)); +static_assert (!std::is_pointer_interconvertible_with_class (&J::j1)); +static_assert (!std::is_pointer_interconvertible_with_class (&J::j3)); +static_assert (!std::is_pointer_interconvertible_with_class (&K::j1)); +static_assert (!std::is_pointer_interconvertible_with_class (&K::j3)); +static_assert (std::is_pointer_interconvertible_with_class (&L::b)); +static_assert (std::is_pointer_interconvertible_with_class<L, int> (&L::b)); +static_assert (std::is_pointer_interconvertible_with_class (&L::b)); +static_assert (std::is_pointer_interconvertible_with_class (&M::d)); +static_assert (!std::is_pointer_interconvertible_with_class (&M::e)); +static_assert (!std::is_pointer_interconvertible_with_class (&M::f)); +static_assert (std::is_pointer_interconvertible_with_class (&U::a)); +static_assert (std::is_pointer_interconvertible_with_class (&U::b)); +static_assert (std::is_pointer_interconvertible_with_class (&U::c)); +static_assert (std::is_pointer_interconvertible_with_class (&V::a)); +static_assert (std::is_pointer_interconvertible_with_class (&V::b)); +static_assert (!std::is_pointer_interconvertible_with_class (&V::c)); +static_assert (std::is_pointer_interconvertible_with_class (&X::a)); +static_assert (std::is_pointer_interconvertible_with_class (&X::b)); +static_assert (std::is_pointer_interconvertible_with_class (&X::c)); +static_assert (std::is_pointer_interconvertible_with_class (&X::d)); +static_assert (!std::is_pointer_interconvertible_with_class ((int B::*) nullptr)); +static_assert (!std::is_pointer_interconvertible_with_class (&Y::foo)); +static_assert (std::is_pointer_interconvertible_with_class (&Z::a)); +static_assert (std::is_pointer_interconvertible_with_class (&Z::d)); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-30 11:19:52.473435926 +0200 @@ -0,0 +1,135 @@ +// P0466R5 +// { dg-do run { target c++20 } } + +namespace std +{ +template <class S, class M> +constexpr bool +is_pointer_interconvertible_with_class (M S::*m) noexcept +{ + return __builtin_is_pointer_interconvertible_with_class (m); +} +} + +struct A; +struct B { int b; double b2; }; +struct C : virtual B { int c; }; +struct D {}; +struct E {}; +struct F : public B, D, E {}; +struct G : public D, E { int g; }; +struct H {}; +struct I : public G, H {}; +struct J { int j1; private: int j2; public: int j3; }; +struct K : public J {}; +struct L : public B, D, E {}; +struct M { D d [[no_unique_address]]; E e [[no_unique_address]]; int f; }; +union U { int a; double b; long long c; }; +struct V { union { int a; long b; }; int c; }; +union X { int a; union { short b; long c; }; long long d; }; +struct Y { void foo () {} }; +union Z { int a; private: int b; protected: int c; public: int d; }; + +int +main () +{ + auto t1 = &B::b; + if (!std::is_pointer_interconvertible_with_class (t1)) + __builtin_abort (); + auto t2 = &B::b2; + if (std::is_pointer_interconvertible_with_class (t2)) + __builtin_abort (); + auto t3 = &C::b; + if (!std::is_pointer_interconvertible_with_class (t3)) + __builtin_abort (); + auto t4 = &F::b; + if (!std::is_pointer_interconvertible_with_class (t4)) + __builtin_abort (); + int F::*t5 = &F::b; + if (!std::is_pointer_interconvertible_with_class (t5)) + __builtin_abort (); + auto t6 = &G::g; + if (!std::is_pointer_interconvertible_with_class (t6)) + __builtin_abort (); + int G::*t7 = &G::g; + if (!std::is_pointer_interconvertible_with_class (t7)) + __builtin_abort (); + auto t8 = &I::g; + if (!std::is_pointer_interconvertible_with_class (t8)) + __builtin_abort (); + int I::*t9 = &I::g; + if (!std::is_pointer_interconvertible_with_class (t9)) + __builtin_abort (); + auto t10 = &J::j1; + if (std::is_pointer_interconvertible_with_class (t10)) + __builtin_abort (); + auto t11 = &J::j3; + if (std::is_pointer_interconvertible_with_class (t11)) + __builtin_abort (); + auto t12 = &K::j1; + if (std::is_pointer_interconvertible_with_class (t12)) + __builtin_abort (); + auto t13 = &K::j3; + if (std::is_pointer_interconvertible_with_class (t13)) + __builtin_abort (); + auto t14 = &L::b; + if (!std::is_pointer_interconvertible_with_class (t14)) + __builtin_abort (); + int L::*t15 = &L::b; + if (!std::is_pointer_interconvertible_with_class (t15)) + __builtin_abort (); + auto t16 = &L::b; + if (!std::is_pointer_interconvertible_with_class (t16)) + __builtin_abort (); + auto t17 = &M::d; + if (!std::is_pointer_interconvertible_with_class (t17)) + __builtin_abort (); + auto t18 = &M::e; + if (std::is_pointer_interconvertible_with_class (t18)) + __builtin_abort (); + auto t19 = &M::f; + if (std::is_pointer_interconvertible_with_class (t19)) + __builtin_abort (); + auto t20 = &U::a; + if (!std::is_pointer_interconvertible_with_class (t20)) + __builtin_abort (); + auto t21 = &U::b; + if (!std::is_pointer_interconvertible_with_class (t21)) + __builtin_abort (); + auto t22 = &U::c; + if (!std::is_pointer_interconvertible_with_class (t22)) + __builtin_abort (); + auto t23 = &V::a; + if (!std::is_pointer_interconvertible_with_class (t23)) + __builtin_abort (); + auto t24 = &V::b; + if (!std::is_pointer_interconvertible_with_class (t24)) + __builtin_abort (); + auto t25 = &V::c; + if (std::is_pointer_interconvertible_with_class (t25)) + __builtin_abort (); + auto t26 = &X::a; + if (!std::is_pointer_interconvertible_with_class (t26)) + __builtin_abort (); + auto t27 = &X::b; + if (!std::is_pointer_interconvertible_with_class (t27)) + __builtin_abort (); + auto t28 = &X::c; + if (!std::is_pointer_interconvertible_with_class (t28)) + __builtin_abort (); + auto t29 = &X::d; + if (!std::is_pointer_interconvertible_with_class (t29)) + __builtin_abort (); + auto t30 = (int B::*) nullptr; + if (std::is_pointer_interconvertible_with_class (t30)) + __builtin_abort (); + auto t31 = &Y::foo; + if (std::is_pointer_interconvertible_with_class (t31)) + __builtin_abort (); + auto t32 = &Z::a; + if (!std::is_pointer_interconvertible_with_class (t32)) + __builtin_abort (); + auto t33 = &Z::d; + if (!std::is_pointer_interconvertible_with_class (t33)) + __builtin_abort (); +} --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C 2021-07-30 11:19:39.553613031 +0200 @@ -0,0 +1,11 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +struct A { int a; }; +struct B; + +bool a = __builtin_is_pointer_interconvertible_with_class (); // { dg-error "needs a single argument" } +bool b = __builtin_is_pointer_interconvertible_with_class (&A::a, &A::a); // { dg-error "needs a single argument" } +bool c = __builtin_is_pointer_interconvertible_with_class (1); // { dg-error "argument is not pointer to member" } +bool d = __builtin_is_pointer_interconvertible_with_class (1.0); // { dg-error "argument is not pointer to member" } +bool e = __builtin_is_pointer_interconvertible_with_class ((int B::*) nullptr); // { dg-error "invalid use of incomplete type" } --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-30 11:19:52.473435926 +0200 @@ -0,0 +1,31 @@ +// P0466R5 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +namespace std +{ +template <class S, class M> +constexpr bool +is_pointer_interconvertible_with_class (M S::*m) noexcept +{ + return __builtin_is_pointer_interconvertible_with_class (m); +} +} + +struct W { struct { int a; long b; }; int c; }; +union X { int a; struct { short b; long c; }; long long d; }; +struct D {}; +struct E { [[no_unique_address]] D e; }; +union Y { int a; struct : public E { short b; long c; }; long long d; }; + +static_assert (std::is_pointer_interconvertible_with_class (&W::a)); +static_assert (!std::is_pointer_interconvertible_with_class (&W::b)); +static_assert (!std::is_pointer_interconvertible_with_class (&W::c)); +static_assert (std::is_pointer_interconvertible_with_class (&X::a)); +static_assert (std::is_pointer_interconvertible_with_class (&X::b)); +static_assert (!std::is_pointer_interconvertible_with_class (&X::c)); +static_assert (std::is_pointer_interconvertible_with_class (&X::d)); +static_assert (std::is_pointer_interconvertible_with_class (&Y::a)); +static_assert (!std::is_pointer_interconvertible_with_class (&Y::b)); +static_assert (!std::is_pointer_interconvertible_with_class (&Y::c)); +static_assert (std::is_pointer_interconvertible_with_class (&Y::d)); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-30 11:19:52.473435926 +0200 @@ -0,0 +1,57 @@ +// P0466R5 +// { dg-do run { target c++20 } } +// { dg-options "" } + +namespace std +{ +template <class S, class M> +constexpr bool +is_pointer_interconvertible_with_class (M S::*m) noexcept +{ + return __builtin_is_pointer_interconvertible_with_class (m); +} +} + +struct W { struct { int a; long b; }; int c; }; +union X { int a; struct { short b; long c; }; long long d; }; +struct D {}; +struct E { [[no_unique_address]] D e; }; +union Y { int a; struct : public E { short b; long c; }; long long d; }; + +int +main () +{ + auto t1 = &W::a; + if (!std::is_pointer_interconvertible_with_class (t1)) + __builtin_abort (); + auto t2 = &W::b; + if (std::is_pointer_interconvertible_with_class (t2)) + __builtin_abort (); + auto t3 = &W::c; + if (std::is_pointer_interconvertible_with_class (t3)) + __builtin_abort (); + auto t4 = &X::a; + if (!std::is_pointer_interconvertible_with_class (t4)) + __builtin_abort (); + auto t5 = &X::b; + if (!std::is_pointer_interconvertible_with_class (t5)) + __builtin_abort (); + auto t6 = &X::c; + if (std::is_pointer_interconvertible_with_class (t6)) + __builtin_abort (); + auto t7 = &X::d; + if (!std::is_pointer_interconvertible_with_class (t7)) + __builtin_abort (); + auto t8 = &Y::a; + if (!std::is_pointer_interconvertible_with_class (t8)) + __builtin_abort (); + auto t9 = &Y::b; + if (std::is_pointer_interconvertible_with_class (t9)) + __builtin_abort (); + auto t10 = &Y::c; + if (std::is_pointer_interconvertible_with_class (t10)) + __builtin_abort (); + auto t11 = &Y::d; + if (!std::is_pointer_interconvertible_with_class (t11)) + __builtin_abort (); +} --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C.jj 2021-07-30 11:19:39.553613031 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C 2021-07-30 11:19:39.553613031 +0200 @@ -0,0 +1,19 @@ +// P0466R5 +// { dg-do compile { target c++20 } } + +namespace std +{ +template <class S, class M> +constexpr bool +is_pointer_interconvertible_with_class (M S::*m) noexcept +{ + return __builtin_is_pointer_interconvertible_with_class (m); +} +} + +struct A { int a; }; + +double A::*a = nullptr; +constexpr double A::*b = nullptr; +constexpr auto c = std::is_pointer_interconvertible_with_class (a); // { dg-error "is not usable in a constant expression" } +constexpr auto d = std::is_pointer_interconvertible_with_class (b); Jakub