* [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] @ 2021-07-29 7:50 Jakub Jelinek 2021-07-29 14:21 ` [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] Jakub Jelinek 2021-07-29 20:38 ` [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jason Merrill 0 siblings, 2 replies; 11+ messages in thread From: Jakub Jelinek @ 2021-07-29 7:50 UTC (permalink / raw) To: Jason Merrill, Jonathan Wakely; +Cc: gcc-patches Hi! The following patch attempts to implement the compiler helpers for libstdc++ std::is_pointer_interconvertible_base_of trait and std::is_pointer_interconvertible_with_class template function. For the former __is_pointer_interconvertible_base_of trait that checks first whether base and derived aren't non-union class types that are the same ignoring toplevel cv-qualifiers, otherwise if derived is unambiguously derived from base without cv-qualifiers, derived being a complete type, and if so, my limited understanding of any derived object being pointer-interconvertible with base subobject IMHO implies (because one can't inherit from unions or unions can't inherit) that we check if derived is standard layout type and we walk bases of derived recursively, stopping on a class that has any non-static data members and check if any of the bases is base. On class with non-static data members no bases are compared already. The latter is implemented using a FE __builtin_is_pointer_interconvertible_with_class, but because on the library side it will be a template function, the builtin takes ... arguments and only during folding verifies it has a single argument with pointer to member type. The initial errors IMHO can only happen if one uses the builtin incorrectly by hand, the template function should ensure that it has exactly a single argument that has pointer to member type. Otherwise, again with my limited understanding of what the template function should do and pointer-interconvertibility, it folds to false for pointer-to-member-function, errors if basetype of the OFFSET_TYPE is incomplete, folds to false for non-std-layout basetype, then finds the first non-static data member in the basetype or its bases (by ignoring DECL_FIELD_IS_BASE FIELD_DECLs that are empty, recursing into DECL_FIELD_IS_BASE FIELD_DECLs type that are non-empty (I think std layout should ensure there is at most one), for unions checks if membertype is same type as any of the union FIELD_DECLs, for non-unions the first other FIELD_DECL only, and for anonymous aggregates similarly (union vs. non-union) but recurses into the anon aggr types. If membertype doesn't match the type of first non-static data member (or for unions any of the members), then the builtin folds to false, otherwise the built folds to a check whether the argument is equal to OFFSET_TYPE of 0 or not, either at compile time if it is constant (e.g. for constexpr folding) or at runtime otherwise. As I wrote in the PR, I've tried my testcases with MSVC on godbolt that claims to implement it, and https://godbolt.org/z/3PnjM33vM for the first testcase shows it disagrees with my expectations on static_assert (std::is_pointer_interconvertible_base_of_v<D, F>); static_assert (std::is_pointer_interconvertible_base_of_v<E, F>); static_assert (!std::is_pointer_interconvertible_base_of_v<D, G>); static_assert (!std::is_pointer_interconvertible_base_of_v<D, I>); static_assert (std::is_pointer_interconvertible_base_of_v<H, volatile I>); Is that a bug in my patch or is MSVC buggy on these (or mix thereof)? https://godbolt.org/z/aYeYnne9d shows the second testcase, here it differs on: static_assert (std::is_pointer_interconvertible_with_class<F, int> (&F::b)); static_assert (std::is_pointer_interconvertible_with_class<I, int> (&I::g)); static_assert (std::is_pointer_interconvertible_with_class<L, int> (&L::b)); static_assert (std::is_pointer_interconvertible_with_class (&V::a)); static_assert (std::is_pointer_interconvertible_with_class (&V::b)); Again, my bug, MSVC bug, mix thereof? Oh, and there is another thing, the standard has an example: struct A { int a; }; // a standard-layout class struct B { int b; }; // a standard-layout class struct C: public A, public B { }; // not a standard-layout class static_assert( is_pointer_interconvertible_with_class( &C::b ) ); // Succeeds because, despite its appearance, &C::b has type // “pointer to member of B of type int”. static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); // Forces the use of class C, and fails. It seems to work as written with MSVC (second assertion fails), but fails with GCC with the patch: /tmp/1.C:22:57: error: no matching function for call to ‘is_pointer_interconvertible_with_class<C>(int B::*)’ 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ /tmp/1.C:8:1: note: candidate: ‘template<class S, class M> constexpr bool std::is_pointer_interconvertible_with_class(M S::*)’ 8 | is_pointer_interconvertible_with_class (M S::*m) noexcept | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /tmp/1.C:8:1: note: template argument deduction/substitution failed: /tmp/1.C:22:57: note: mismatched types ‘C’ and ‘B’ 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ the second int argument isn't deduced. This boils down to: template <class S, class M> bool foo (M S::*m) noexcept; struct A { int a; }; struct B { int b; }; struct C : public A, public B {}; bool a = foo (&C::b); bool b = foo<C, int> (&C::b); bool c = foo<C> (&C::b); which with /std:c++20 or -std=c++20 is accepted by latest MSVC and ICC but rejected by GCC and clang (in both cases on the last line). Is this a GCC/clang bug in argument deduction (in that case I think we want a separate PR), or a bug in ICC/MSVC and the standard itself that should specify in the examples both template arguments instead of just the first? Now that I'm writing the above text and rereading the pointer-interconvertibility definition, I think my first_nonstatic_data_member_p and fold_builtin_is_pointer_inverconvertible_with_class have one bug, for unions the pointer inter-convertibility doesn't talk about std layout at all, so I think I need to check for std_layout_type_p only for non-union class types and accept any union, std_layout_type_p or not. But when recursing from a union type into anonymous structure type punt if the anonymous structure type is not std_layout_type_p + add testcase coverage. Anyway, the patch has been bootstrapped/regtested on x86_64-linux and i686-linux. 2021-07-29 Jakub Jelinek <jakub@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_1, 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-21 18:55:05.239627937 +0200 +++ gcc/c-family/c-common.h 2021-07-28 12:07:31.717700448 +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-21 18:55:05.239627937 +0200 +++ gcc/c-family/c-common.c 2021-07-28 12:08:26.419963865 +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-21 18:55:05.326626722 +0200 +++ gcc/cp/cp-tree.h 2021-07-28 14:08:55.920562235 +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-27 09:47:44.098780350 +0200 +++ gcc/cp/parser.c 2021-07-28 12:10:24.669370137 +0200 @@ -5797,6 +5797,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: @@ -10686,6 +10687,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-21 18:55:05.301627072 +0200 +++ gcc/cp/cp-objcp-common.c 2021-07-28 12:10:40.358158672 +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-21 18:55:05.281627351 +0200 +++ gcc/cp/constraint.cc 2021-07-28 12:12:31.875655576 +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-21 18:55:05.362626220 +0200 +++ gcc/cp/decl.c 2021-07-28 13:35:55.640239697 +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-21 18:55:05.256627699 +0200 +++ gcc/cp/constexpr.c 2021-07-28 17:12:01.796428596 +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-22 12:37:20.450532703 +0200 +++ gcc/cp/semantics.c 2021-07-28 16:51:05.119363473 +0200 @@ -10566,6 +10566,135 @@ classtype_has_nothrow_assign_or_copy_p ( return saw_copy; } +/* 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; + + unsigned ix; + tree base_binfo; + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) + if (SAME_BINFO_TYPE_P (BINFO_TYPE (base_binfo), base) + || pointer_interconvertible_base_of_p_1 (base_binfo, base)) + return true; + return false; +} + +/* 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 = cp_build_qualified_type (base, TYPE_UNQUALIFIED); + derived = cp_build_qualified_type (derived, TYPE_UNQUALIFIED); + if (NON_UNION_CLASS_TYPE_P (base) + && NON_UNION_CLASS_TYPE_P (derived) + && same_type_p (base, derived)) + return true; + + if (!complete_type_or_else (derived, NULL_TREE)) + return false; + + if (!uniquely_derived_from_p (base, derived)) + return false; + + if (!std_layout_type_p (derived)) + return false; + + return pointer_interconvertible_base_of_p_1 (TYPE_BINFO (derived), base); +} + +/* 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) + return first_nonstatic_data_member_p (TREE_TYPE (field), + membertype); + if (first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) + return true; + } + else if (TREE_CODE (type) != UNION_TYPE) + return same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), + membertype); + else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), + membertype)) + return true; + } + 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 (!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 +10788,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 +10918,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 +10936,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-28 12:06:00.482928952 +0200 +++ gcc/cp/cp-gimplify.c 2021-07-28 14:49:06.034027853 +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-21 18:55:05.416625466 +0200 +++ gcc/cp/tree.c 2021-07-28 14:07:41.181568566 +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-21 18:55:05.344626471 +0200 +++ gcc/cp/cxx-pretty-print.c 2021-07-28 12:14:20.675189115 +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-28 13:21:41.654721641 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C 2021-07-28 13:21:20.562005151 +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-28 17:23:23.353247589 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-28 17:36:10.514918765 +0200 @@ -0,0 +1,62 @@ +// 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 () {} }; + +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)); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C.jj 2021-07-28 17:31:11.981938196 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-28 17:39:45.458022541 +0200 @@ -0,0 +1,128 @@ +// 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 () {} }; + +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 (); +} --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C.jj 2021-07-28 17:31:19.863832080 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C 2021-07-28 17:34:52.767965633 +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-28 17:36:30.412650659 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-28 17:37:30.435841885 +0200 @@ -0,0 +1,24 @@ +// 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; }; + +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)); --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C.jj 2021-07-28 17:36:35.294584876 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-28 17:39:08.795516549 +0200 @@ -0,0 +1,42 @@ +// 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; }; + +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 (); +} --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C.jj 2021-05-25 17:45:05.138850892 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C 2021-07-28 17:53:43.244733852 +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 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] 2021-07-29 7:50 [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jakub Jelinek @ 2021-07-29 14:21 ` Jakub Jelinek 2021-07-30 5:10 ` Jason Merrill 2021-07-29 20:38 ` [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jason Merrill 1 sibling, 1 reply; 11+ messages in thread From: Jakub Jelinek @ 2021-07-29 14:21 UTC (permalink / raw) To: Jason Merrill, Jonathan Wakely; +Cc: gcc-patches On Thu, Jul 29, 2021 at 09:50:10AM +0200, Jakub Jelinek via Gcc-patches wrote: > Now that I'm writing the above text and rereading the > pointer-interconvertibility definition, I think my first_nonstatic_data_member_p > and fold_builtin_is_pointer_inverconvertible_with_class have one bug, > for unions the pointer inter-convertibility doesn't talk about std layout at > all, so I think I need to check for std_layout_type_p only for non-union > class types and accept any union, std_layout_type_p or not. But when > recursing from a union type into anonymous structure type punt if the > anonymous structure type is not std_layout_type_p + add testcase coverage. For this part, here is an incremental fix. Tested on x86_64-linux. It also shows that in the case (we're beyond the standard in this case because anonymous structures are not in the standard) of union with non-std-layout anonymous structure in it, in the case in the testcases like: struct D {}; struct E { [[no_unique_address]] D e; }; union Y { int a; struct : public E { short b; long c; }; long long d; }; the builtin will return false for &Y::b - while &Y::b is at offset zero, the anonymous structure is not std-layout and therefore the pointer-interconvertibility rules say pointers aren't interconvertible. But in case like: union Y2 { int a; struct : public E { int b; long c; }; long long d; }; it will return true for &Y2::b - while the same applies, there is another union member with int type. In theory when seeing the PTRMEM_CST we could still differentiate, &Y2::a is ok but &Y2::b is not. But as soon as we have just an INTEGER_CST with OFFSET_TYPE or need to check it at runtime, all we know is that we have pointer to int data member in Y2 at offset 0, and that is the same for &Y2::a and &Y2::b. 2021-07-29 Jakub Jelinek <jakub@redhat.com> PR c++/101539 * semantics.c (first_nonstatic_data_member_p): Don't recurse into non-std-layout non-union class types from union type. (fold_builtin_is_pointer_inverconvertible_with_class): Don't check std-layout type for union types. * g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C: Add tests for non-std-layout union type. * g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C: Likewise. * g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C: Add tests for non-std-layout anonymous class type in union. * g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C: Likewise. --- gcc/cp/semantics.c.jj 2021-07-28 23:06:38.665443459 +0200 +++ gcc/cp/semantics.c 2021-07-29 15:44:30.659713391 +0200 @@ -10631,7 +10631,9 @@ first_nonstatic_data_member_p (tree type if (TREE_CODE (type) != UNION_TYPE) return first_nonstatic_data_member_p (TREE_TYPE (field), membertype); - if (first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) + if ((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 (TREE_CODE (type) != UNION_TYPE) @@ -10677,7 +10679,8 @@ fold_builtin_is_pointer_inverconvertible if (!complete_type_or_else (basetype, NULL_TREE)) return boolean_false_node; - if (!std_layout_type_p (basetype)) + if (TREE_CODE (basetype) != UNION_TYPE + && !std_layout_type_p (basetype)) return boolean_false_node; if (!first_nonstatic_data_member_p (basetype, membertype)) --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C.jj 2021-07-28 23:06:38.667443431 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-29 15:46:55.809743808 +0200 @@ -28,6 +28,7 @@ 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)); @@ -60,3 +61,5 @@ static_assert (std::is_pointer_interconv 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-28 23:06:38.667443431 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-29 15:48:33.075423974 +0200 @@ -28,6 +28,7 @@ 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 () @@ -125,4 +126,10 @@ main () 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-class4.C.jj 2021-07-28 23:06:38.667443431 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-29 15:56:54.340622140 +0200 @@ -14,6 +14,9 @@ is_pointer_interconvertible_with_class ( 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)); @@ -22,3 +25,7 @@ static_assert (std::is_pointer_interconv 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-28 23:06:38.667443431 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-29 15:57:10.829398399 +0200 @@ -14,6 +14,9 @@ is_pointer_interconvertible_with_class ( 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 () @@ -39,4 +42,16 @@ main () 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 (); } Jakub ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] 2021-07-29 14:21 ` [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] Jakub Jelinek @ 2021-07-30 5:10 ` Jason Merrill 2021-07-30 7:57 ` Jakub Jelinek 2021-07-30 9:17 ` Jakub Jelinek 0 siblings, 2 replies; 11+ messages in thread From: Jason Merrill @ 2021-07-30 5:10 UTC (permalink / raw) To: Jakub Jelinek, Jonathan Wakely; +Cc: gcc-patches On 7/29/21 10:21 AM, Jakub Jelinek wrote: > On Thu, Jul 29, 2021 at 09:50:10AM +0200, Jakub Jelinek via Gcc-patches wrote: >> Now that I'm writing the above text and rereading the >> pointer-interconvertibility definition, I think my first_nonstatic_data_member_p >> and fold_builtin_is_pointer_inverconvertible_with_class have one bug, >> for unions the pointer inter-convertibility doesn't talk about std layout at >> all, so I think I need to check for std_layout_type_p only for non-union >> class types and accept any union, std_layout_type_p or not. But when >> recursing from a union type into anonymous structure type punt if the >> anonymous structure type is not std_layout_type_p + add testcase coverage. > > For this part, here is an incremental fix. Tested on x86_64-linux. > > It also shows that in the case (we're beyond the standard in this case > because anonymous structures are not in the standard) of union with > non-std-layout anonymous structure in it, in the case in the testcases like: > struct D {}; > struct E { [[no_unique_address]] D e; }; > union Y { int a; struct : public E { short b; long c; }; long long d; }; We don't already reject an anonymous struct with bases? I think we should do so, in fixup_anonymous_aggr. We might even require anonymous structs to be standard-layout. > the builtin will return false for &Y::b - while &Y::b is at offset zero, > the anonymous structure is not std-layout and therefore the > pointer-interconvertibility rules say pointers aren't interconvertible. > But in case like: > union Y2 { int a; struct : public E { int b; long c; }; long long d; }; > it will return true for &Y2::b - while the same applies, there is > another union member with int type. In theory when seeing the PTRMEM_CST > we could still differentiate, &Y2::a is ok but &Y2::b is not. But as soon > as we have just an INTEGER_CST with OFFSET_TYPE or need to check it at > runtime, all we know is that we have pointer to int data member in Y2 > at offset 0, and that is the same for &Y2::a and &Y2::b. Yep. I'm inclined not to handle this extension case specifically. > 2021-07-29 Jakub Jelinek <jakub@redhat.com> > > PR c++/101539 > * semantics.c (first_nonstatic_data_member_p): Don't recurse into > non-std-layout non-union class types from union type. > (fold_builtin_is_pointer_inverconvertible_with_class): Don't check > std-layout type for union types. > > * g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C: Add > tests for non-std-layout union type. > * g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C: Likewise. > * g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C: Add > tests for non-std-layout anonymous class type in union. > * g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C: Likewise. > > --- gcc/cp/semantics.c.jj 2021-07-28 23:06:38.665443459 +0200 > +++ gcc/cp/semantics.c 2021-07-29 15:44:30.659713391 +0200 > @@ -10631,7 +10631,9 @@ first_nonstatic_data_member_p (tree type > if (TREE_CODE (type) != UNION_TYPE) > return first_nonstatic_data_member_p (TREE_TYPE (field), > membertype); > - if (first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) > + if ((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 (TREE_CODE (type) != UNION_TYPE) > @@ -10677,7 +10679,8 @@ fold_builtin_is_pointer_inverconvertible > if (!complete_type_or_else (basetype, NULL_TREE)) > return boolean_false_node; > > - if (!std_layout_type_p (basetype)) > + if (TREE_CODE (basetype) != UNION_TYPE > + && !std_layout_type_p (basetype)) > return boolean_false_node; > > if (!first_nonstatic_data_member_p (basetype, membertype)) > --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C.jj 2021-07-28 23:06:38.667443431 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-29 15:46:55.809743808 +0200 > @@ -28,6 +28,7 @@ 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)); > @@ -60,3 +61,5 @@ static_assert (std::is_pointer_interconv > 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-28 23:06:38.667443431 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-29 15:48:33.075423974 +0200 > @@ -28,6 +28,7 @@ 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 () > @@ -125,4 +126,10 @@ main () > 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-class4.C.jj 2021-07-28 23:06:38.667443431 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-29 15:56:54.340622140 +0200 > @@ -14,6 +14,9 @@ is_pointer_interconvertible_with_class ( > > 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)); > @@ -22,3 +25,7 @@ static_assert (std::is_pointer_interconv > 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-28 23:06:38.667443431 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-29 15:57:10.829398399 +0200 > @@ -14,6 +14,9 @@ is_pointer_interconvertible_with_class ( > > 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 () > @@ -39,4 +42,16 @@ main () > 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 (); > } > > Jakub > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] 2021-07-30 5:10 ` Jason Merrill @ 2021-07-30 7:57 ` Jakub Jelinek 2021-07-30 9:17 ` Jakub Jelinek 1 sibling, 0 replies; 11+ messages in thread From: Jakub Jelinek @ 2021-07-30 7:57 UTC (permalink / raw) To: Jason Merrill; +Cc: Jonathan Wakely, gcc-patches On Fri, Jul 30, 2021 at 01:10:33AM -0400, Jason Merrill wrote: > On 7/29/21 10:21 AM, Jakub Jelinek wrote: > > On Thu, Jul 29, 2021 at 09:50:10AM +0200, Jakub Jelinek via Gcc-patches wrote: > > > Now that I'm writing the above text and rereading the > > > pointer-interconvertibility definition, I think my first_nonstatic_data_member_p > > > and fold_builtin_is_pointer_inverconvertible_with_class have one bug, > > > for unions the pointer inter-convertibility doesn't talk about std layout at > > > all, so I think I need to check for std_layout_type_p only for non-union > > > class types and accept any union, std_layout_type_p or not. But when > > > recursing from a union type into anonymous structure type punt if the > > > anonymous structure type is not std_layout_type_p + add testcase coverage. > > > > For this part, here is an incremental fix. Tested on x86_64-linux. > > > > It also shows that in the case (we're beyond the standard in this case > > because anonymous structures are not in the standard) of union with > > non-std-layout anonymous structure in it, in the case in the testcases like: > > struct D {}; > > struct E { [[no_unique_address]] D e; }; > > union Y { int a; struct : public E { short b; long c; }; long long d; }; > > We don't already reject an anonymous struct with bases? I think we should > do so, in fixup_anonymous_aggr. We might even require anonymous structs to > be standard-layout. Apparently not, the above is accepted. I was looking for an example of non-stdlayout anon aggregate in union and my first try (mixing private/public members) has been rejected. > I'm inclined not to handle this extension case specifically. You mean not to even recurse into anonymous structures in the function, or something else? Jakub ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] 2021-07-30 5:10 ` Jason Merrill 2021-07-30 7:57 ` Jakub Jelinek @ 2021-07-30 9:17 ` Jakub Jelinek 1 sibling, 0 replies; 11+ messages in thread From: Jakub Jelinek @ 2021-07-30 9:17 UTC (permalink / raw) To: Jason Merrill; +Cc: Jonathan Wakely, gcc-patches On Fri, Jul 30, 2021 at 01:10:33AM -0400, Jason Merrill wrote: > > It also shows that in the case (we're beyond the standard in this case > > because anonymous structures are not in the standard) of union with > > non-std-layout anonymous structure in it, in the case in the testcases like: > > struct D {}; > > struct E { [[no_unique_address]] D e; }; > > union Y { int a; struct : public E { short b; long c; }; long long d; }; > > We don't already reject an anonymous struct with bases? I think we should > do so, in fixup_anonymous_aggr. We might even require anonymous structs to > be standard-layout. Not having base classes seems reasonable requirement for the anonymous structures, after all, I couldn't find a way to refer to the members in the base class - &Y::e is rejected with the above. But standard layout means that even all the non-static members of the struct need to be standard-layout, that seems an unnecessary requirement for anon structures to me. Jakub ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-29 7:50 [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jakub Jelinek 2021-07-29 14:21 ` [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] Jakub Jelinek @ 2021-07-29 20:38 ` Jason Merrill 2021-07-30 5:41 ` Jason Merrill 2021-07-30 9:51 ` Jakub Jelinek 1 sibling, 2 replies; 11+ messages in thread From: Jason Merrill @ 2021-07-29 20:38 UTC (permalink / raw) To: Jakub Jelinek, Jonathan Wakely; +Cc: gcc-patches On 7/29/21 3:50 AM, Jakub Jelinek wrote: > Hi! > > The following patch attempts to implement the compiler helpers for > libstdc++ std::is_pointer_interconvertible_base_of trait and > std::is_pointer_interconvertible_with_class template function. > > For the former __is_pointer_interconvertible_base_of trait that checks first > whether base and derived aren't non-union class types that are the same > ignoring toplevel cv-qualifiers, otherwise if derived is unambiguously > derived from base without cv-qualifiers, derived being a complete type, > and if so, my limited understanding of any derived object being > pointer-interconvertible with base subobject IMHO implies (because one can't > inherit from unions or unions can't inherit) that we check if derived is > standard layout type and we walk bases of derived > recursively, stopping on a class that has any non-static data members and > check if any of the bases is base. On class with non-static data members > no bases are compared already. > > The latter is implemented using a FE > __builtin_is_pointer_interconvertible_with_class, but because on the library > side it will be a template function, the builtin takes ... arguments and > only during folding verifies it has a single argument with pointer to member > type. The initial errors IMHO can only happen if one uses the builtin > incorrectly by hand, the template function should ensure that it has > exactly a single argument that has pointer to member type. > Otherwise, again with my limited understanding of what > the template function should do and pointer-interconvertibility, > it folds to false for pointer-to-member-function, errors if > basetype of the OFFSET_TYPE is incomplete, folds to false > for non-std-layout basetype, then finds the first non-static > data member in the basetype or its bases (by ignoring > DECL_FIELD_IS_BASE FIELD_DECLs that are empty, recursing into > DECL_FIELD_IS_BASE FIELD_DECLs type that are non-empty (I think > std layout should ensure there is at most one), for unions > checks if membertype is same type as any of the union FIELD_DECLs, > for non-unions the first other FIELD_DECL only, and for anonymous > aggregates similarly (union vs. non-union) but recurses into the > anon aggr types. If membertype doesn't match the type of > first non-static data member (or for unions any of the members), > then the builtin folds to false, otherwise the built folds to > a check whether the argument is equal to OFFSET_TYPE of 0 or not, > either at compile time if it is constant (e.g. for constexpr > folding) or at runtime otherwise. > > As I wrote in the PR, I've tried my testcases with MSVC on godbolt > that claims to implement it, and https://godbolt.org/z/3PnjM33vM > for the first testcase shows it disagrees with my expectations on > static_assert (std::is_pointer_interconvertible_base_of_v<D, F>); > static_assert (std::is_pointer_interconvertible_base_of_v<E, F>); > static_assert (!std::is_pointer_interconvertible_base_of_v<D, G>); > static_assert (!std::is_pointer_interconvertible_base_of_v<D, I>); > static_assert (std::is_pointer_interconvertible_base_of_v<H, volatile I>); > Is that a bug in my patch or is MSVC buggy on these (or mix thereof)? > https://godbolt.org/z/aYeYnne9d > shows the second testcase, here it differs on: > static_assert (std::is_pointer_interconvertible_with_class<F, int> (&F::b)); > static_assert (std::is_pointer_interconvertible_with_class<I, int> (&I::g)); > static_assert (std::is_pointer_interconvertible_with_class<L, int> (&L::b)); > static_assert (std::is_pointer_interconvertible_with_class (&V::a)); > static_assert (std::is_pointer_interconvertible_with_class (&V::b)); > Again, my bug, MSVC bug, mix thereof? > > Oh, and there is another thing, the standard has an example: > struct A { int a; }; // a standard-layout class > struct B { int b; }; // a standard-layout class > struct C: public A, public B { }; // not a standard-layout class > > static_assert( is_pointer_interconvertible_with_class( &C::b ) ); > // Succeeds because, despite its appearance, &C::b has type > // “pointer to member of B of type int”. > static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); > // Forces the use of class C, and fails. > It seems to work as written with MSVC (second assertion fails), > but fails with GCC with the patch: > /tmp/1.C:22:57: error: no matching function for call to ‘is_pointer_interconvertible_with_class<C>(int B::*)’ > 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); > | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ > /tmp/1.C:8:1: note: candidate: ‘template<class S, class M> constexpr bool std::is_pointer_interconvertible_with_class(M S::*)’ > 8 | is_pointer_interconvertible_with_class (M S::*m) noexcept > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /tmp/1.C:8:1: note: template argument deduction/substitution failed: > /tmp/1.C:22:57: note: mismatched types ‘C’ and ‘B’ > 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); > | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ > the second int argument isn't deduced. > > This boils down to: > template <class S, class M> > bool foo (M S::*m) noexcept; > struct A { int a; }; > struct B { int b; }; > struct C : public A, public B {}; > bool a = foo (&C::b); > bool b = foo<C, int> (&C::b); > bool c = foo<C> (&C::b); > which with /std:c++20 or -std=c++20 is accepted by latest MSVC and ICC but > rejected by GCC and clang (in both cases on the last line). > Is this a GCC/clang bug in argument deduction (in that case I think we want > a separate PR), or a bug in ICC/MSVC and the standard itself that should > specify in the examples both template arguments instead of just the first? I'll look into this. > Now that I'm writing the above text and rereading the > pointer-interconvertibility definition, I think my first_nonstatic_data_member_p > and fold_builtin_is_pointer_inverconvertible_with_class have one bug, > for unions the pointer inter-convertibility doesn't talk about std layout at > all, so I think I need to check for std_layout_type_p only for non-union > class types and accept any union, std_layout_type_p or not. But when > recursing from a union type into anonymous structure type punt if the > anonymous structure type is not std_layout_type_p + add testcase coverage. > > Anyway, the patch has been bootstrapped/regtested on x86_64-linux and > i686-linux. > > 2021-07-29 Jakub Jelinek <jakub@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_1, > 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-21 18:55:05.239627937 +0200 > +++ gcc/c-family/c-common.h 2021-07-28 12:07:31.717700448 +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-21 18:55:05.239627937 +0200 > +++ gcc/c-family/c-common.c 2021-07-28 12:08:26.419963865 +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-21 18:55:05.326626722 +0200 > +++ gcc/cp/cp-tree.h 2021-07-28 14:08:55.920562235 +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-27 09:47:44.098780350 +0200 > +++ gcc/cp/parser.c 2021-07-28 12:10:24.669370137 +0200 > @@ -5797,6 +5797,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: > @@ -10686,6 +10687,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-21 18:55:05.301627072 +0200 > +++ gcc/cp/cp-objcp-common.c 2021-07-28 12:10:40.358158672 +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-21 18:55:05.281627351 +0200 > +++ gcc/cp/constraint.cc 2021-07-28 12:12:31.875655576 +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-21 18:55:05.362626220 +0200 > +++ gcc/cp/decl.c 2021-07-28 13:35:55.640239697 +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-21 18:55:05.256627699 +0200 > +++ gcc/cp/constexpr.c 2021-07-28 17:12:01.796428596 +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-22 12:37:20.450532703 +0200 > +++ gcc/cp/semantics.c 2021-07-28 16:51:05.119363473 +0200 > @@ -10566,6 +10566,135 @@ classtype_has_nothrow_assign_or_copy_p ( > return saw_copy; > } > > +/* 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. > + unsigned ix; > + tree base_binfo; > + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) > + if (SAME_BINFO_TYPE_P (BINFO_TYPE (base_binfo), base) > + || pointer_interconvertible_base_of_p_1 (base_binfo, base)) > + return true; > + return false; > +} > + > +/* 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 = cp_build_qualified_type (base, TYPE_UNQUALIFIED); > + derived = cp_build_qualified_type (derived, TYPE_UNQUALIFIED); These could be TYPE_MAIN_VARIANT. > + if (NON_UNION_CLASS_TYPE_P (base) > + && NON_UNION_CLASS_TYPE_P (derived) If either is not a non-union class, we should return false immediately. I don't know what the rationale was for returning false for arguments that are the same union, but that decision was made earlier, for std::is_base_of. > + && same_type_p (base, derived)) > + return true; > + > + if (!complete_type_or_else (derived, NULL_TREE)) > + return false; It's an error if derived is incomplete (again, like is_base_of). finish_trait_expr should have already handled that. > + if (!uniquely_derived_from_p (base, derived)) > + return false; > + > + if (!std_layout_type_p (derived)) > + return false; I might move this check above derived_from_p, as it's just checking a flag. > + return pointer_interconvertible_base_of_p_1 (TYPE_BINFO (derived), base); > +} > + > +/* 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) > + return first_nonstatic_data_member_p (TREE_TYPE (field), > + membertype); > + if (first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) > + return true; > + } > + else if (TREE_CODE (type) != UNION_TYPE) > + return same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), > + membertype); > + else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), > + membertype)) > + return true; Instead of checking !UNION_TYPE twice above, you could check it once here and return false. More later. > + } > + 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 (!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 +10788,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 +10918,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 +10936,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-28 12:06:00.482928952 +0200 > +++ gcc/cp/cp-gimplify.c 2021-07-28 14:49:06.034027853 +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-21 18:55:05.416625466 +0200 > +++ gcc/cp/tree.c 2021-07-28 14:07:41.181568566 +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-21 18:55:05.344626471 +0200 > +++ gcc/cp/cxx-pretty-print.c 2021-07-28 12:14:20.675189115 +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-28 13:21:41.654721641 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C 2021-07-28 13:21:20.562005151 +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-28 17:23:23.353247589 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-28 17:36:10.514918765 +0200 > @@ -0,0 +1,62 @@ > +// 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 () {} }; > + > +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)); > --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C.jj 2021-07-28 17:31:11.981938196 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-28 17:39:45.458022541 +0200 > @@ -0,0 +1,128 @@ > +// 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 () {} }; > + > +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 (); > +} > --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C.jj 2021-07-28 17:31:19.863832080 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C 2021-07-28 17:34:52.767965633 +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-28 17:36:30.412650659 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-28 17:37:30.435841885 +0200 > @@ -0,0 +1,24 @@ > +// 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; }; > + > +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)); > --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C.jj 2021-07-28 17:36:35.294584876 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-28 17:39:08.795516549 +0200 > @@ -0,0 +1,42 @@ > +// 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; }; > + > +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 (); > +} > --- gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C.jj 2021-05-25 17:45:05.138850892 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C 2021-07-28 17:53:43.244733852 +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 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-29 20:38 ` [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jason Merrill @ 2021-07-30 5:41 ` Jason Merrill 2021-07-30 9:51 ` Jakub Jelinek 1 sibling, 0 replies; 11+ messages in thread From: Jason Merrill @ 2021-07-30 5:41 UTC (permalink / raw) To: Jakub Jelinek, Jonathan Wakely; +Cc: gcc-patches On 7/29/21 4:38 PM, Jason Merrill wrote: > On 7/29/21 3:50 AM, Jakub Jelinek wrote: >> Hi! >> >> The following patch attempts to implement the compiler helpers for >> libstdc++ std::is_pointer_interconvertible_base_of trait and >> std::is_pointer_interconvertible_with_class template function. >> >> For the former __is_pointer_interconvertible_base_of trait that checks >> first >> whether base and derived aren't non-union class types that are the same >> ignoring toplevel cv-qualifiers, otherwise if derived is unambiguously >> derived from base without cv-qualifiers, derived being a complete type, >> and if so, my limited understanding of any derived object being >> pointer-interconvertible with base subobject IMHO implies (because one >> can't >> inherit from unions or unions can't inherit) that we check if derived is >> standard layout type and we walk bases of derived >> recursively, stopping on a class that has any non-static data members and >> check if any of the bases is base. On class with non-static data members >> no bases are compared already. >> >> The latter is implemented using a FE >> __builtin_is_pointer_interconvertible_with_class, but because on the >> library >> side it will be a template function, the builtin takes ... arguments and >> only during folding verifies it has a single argument with pointer to >> member >> type. The initial errors IMHO can only happen if one uses the builtin >> incorrectly by hand, the template function should ensure that it has >> exactly a single argument that has pointer to member type. >> Otherwise, again with my limited understanding of what >> the template function should do and pointer-interconvertibility, >> it folds to false for pointer-to-member-function, errors if >> basetype of the OFFSET_TYPE is incomplete, folds to false >> for non-std-layout basetype, then finds the first non-static >> data member in the basetype or its bases (by ignoring >> DECL_FIELD_IS_BASE FIELD_DECLs that are empty, recursing into >> DECL_FIELD_IS_BASE FIELD_DECLs type that are non-empty (I think >> std layout should ensure there is at most one), for unions >> checks if membertype is same type as any of the union FIELD_DECLs, >> for non-unions the first other FIELD_DECL only, and for anonymous >> aggregates similarly (union vs. non-union) but recurses into the >> anon aggr types. If membertype doesn't match the type of >> first non-static data member (or for unions any of the members), >> then the builtin folds to false, otherwise the built folds to >> a check whether the argument is equal to OFFSET_TYPE of 0 or not, >> either at compile time if it is constant (e.g. for constexpr >> folding) or at runtime otherwise. >> >> As I wrote in the PR, I've tried my testcases with MSVC on godbolt >> that claims to implement it, and https://godbolt.org/z/3PnjM33vM >> for the first testcase shows it disagrees with my expectations on >> static_assert (std::is_pointer_interconvertible_base_of_v<D, F>); >> static_assert (std::is_pointer_interconvertible_base_of_v<E, F>); I think these are correct. >> static_assert (!std::is_pointer_interconvertible_base_of_v<D, G>); >> static_assert (!std::is_pointer_interconvertible_base_of_v<D, I>); >> static_assert (std::is_pointer_interconvertible_base_of_v<H, volatile >> I>); I think these are wrong, given my comment below about CWG2254. >> Is that a bug in my patch or is MSVC buggy on these (or mix thereof)? >> https://godbolt.org/z/aYeYnne9d >> shows the second testcase, here it differs on: >> static_assert (std::is_pointer_interconvertible_with_class<F, int> >> (&F::b)); >> static_assert (std::is_pointer_interconvertible_with_class<I, int> >> (&I::g)); >> static_assert (std::is_pointer_interconvertible_with_class<L, int> >> (&L::b)); >> static_assert (std::is_pointer_interconvertible_with_class (&V::a)); >> static_assert (std::is_pointer_interconvertible_with_class (&V::b)); >> Again, my bug, MSVC bug, mix thereof? MSVC bug, I think. >> Oh, and there is another thing, the standard has an example: >> struct A { int a; }; // a standard-layout class >> struct B { int b; }; // a standard-layout class >> struct C: public A, public B { }; // not a standard-layout class >> >> static_assert( is_pointer_interconvertible_with_class( &C::b ) ); >> // Succeeds because, despite its appearance, &C::b has type >> // “pointer to member of B of type int”. >> static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); >> // Forces the use of class C, and fails. >> It seems to work as written with MSVC (second assertion fails), >> but fails with GCC with the patch: >> /tmp/1.C:22:57: error: no matching function for call to >> ‘is_pointer_interconvertible_with_class<C>(int B::*)’ >> 22 | static_assert( is_pointer_interconvertible_with_class<C>( >> &C::b ) ); >> | >> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ >> /tmp/1.C:8:1: note: candidate: ‘template<class S, class M> constexpr >> bool std::is_pointer_interconvertible_with_class(M S::*)’ >> 8 | is_pointer_interconvertible_with_class (M S::*m) noexcept >> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> /tmp/1.C:8:1: note: template argument deduction/substitution failed: >> /tmp/1.C:22:57: note: mismatched types ‘C’ and ‘B’ >> 22 | static_assert( is_pointer_interconvertible_with_class<C>( >> &C::b ) ); >> | >> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ >> the second int argument isn't deduced. >> >> This boils down to: >> template <class S, class M> >> bool foo (M S::*m) noexcept; >> struct A { int a; }; >> struct B { int b; }; >> struct C : public A, public B {}; >> bool a = foo (&C::b); >> bool b = foo<C, int> (&C::b); >> bool c = foo<C> (&C::b); >> which with /std:c++20 or -std=c++20 is accepted by latest MSVC and ICC >> but >> rejected by GCC and clang (in both cases on the last line). >> Is this a GCC/clang bug in argument deduction (in that case I think we >> want >> a separate PR), or a bug in ICC/MSVC and the standard itself that should >> specify in the examples both template arguments instead of just the >> first? > > I'll look into this. I think GCC/clang are correct here, I've raised this with CWG. >> Now that I'm writing the above text and rereading the >> pointer-interconvertibility definition, I think my >> first_nonstatic_data_member_p >> and fold_builtin_is_pointer_inverconvertible_with_class have one bug, >> for unions the pointer inter-convertibility doesn't talk about std >> layout at >> all, so I think I need to check for std_layout_type_p only for non-union >> class types and accept any union, std_layout_type_p or not. But when >> recursing from a union type into anonymous structure type punt if the >> anonymous structure type is not std_layout_type_p + add testcase >> coverage. >> >> Anyway, the patch has been bootstrapped/regtested on x86_64-linux and >> i686-linux. >> >> 2021-07-29 Jakub Jelinek <jakub@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_1, >> 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-21 18:55:05.239627937 +0200 >> +++ gcc/c-family/c-common.h 2021-07-28 12:07:31.717700448 +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-21 18:55:05.239627937 +0200 >> +++ gcc/c-family/c-common.c 2021-07-28 12:08:26.419963865 +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-21 18:55:05.326626722 +0200 >> +++ gcc/cp/cp-tree.h 2021-07-28 14:08:55.920562235 +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-27 09:47:44.098780350 +0200 >> +++ gcc/cp/parser.c 2021-07-28 12:10:24.669370137 +0200 >> @@ -5797,6 +5797,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: >> @@ -10686,6 +10687,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-21 18:55:05.301627072 +0200 >> +++ gcc/cp/cp-objcp-common.c 2021-07-28 12:10:40.358158672 +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-21 18:55:05.281627351 +0200 >> +++ gcc/cp/constraint.cc 2021-07-28 12:12:31.875655576 +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-21 18:55:05.362626220 +0200 >> +++ gcc/cp/decl.c 2021-07-28 13:35:55.640239697 +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-21 18:55:05.256627699 +0200 >> +++ gcc/cp/constexpr.c 2021-07-28 17:12:01.796428596 +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-22 12:37:20.450532703 +0200 >> +++ gcc/cp/semantics.c 2021-07-28 16:51:05.119363473 +0200 >> @@ -10566,6 +10566,135 @@ classtype_has_nothrow_assign_or_copy_p ( >> return saw_copy; >> } >> +/* 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. > >> + unsigned ix; >> + tree base_binfo; >> + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) >> + if (SAME_BINFO_TYPE_P (BINFO_TYPE (base_binfo), base) >> + || pointer_interconvertible_base_of_p_1 (base_binfo, base)) >> + return true; >> + return false; >> +} >> + >> +/* 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 = cp_build_qualified_type (base, TYPE_UNQUALIFIED); >> + derived = cp_build_qualified_type (derived, TYPE_UNQUALIFIED); > > These could be TYPE_MAIN_VARIANT. > >> + if (NON_UNION_CLASS_TYPE_P (base) >> + && NON_UNION_CLASS_TYPE_P (derived) > > If either is not a non-union class, we should return false immediately. > I don't know what the rationale was for returning false for arguments > that are the same union, but that decision was made earlier, for > std::is_base_of. > >> + && same_type_p (base, derived)) >> + return true; >> + >> + if (!complete_type_or_else (derived, NULL_TREE)) >> + return false; > > It's an error if derived is incomplete (again, like is_base_of). > finish_trait_expr should have already handled that. > >> + if (!uniquely_derived_from_p (base, derived)) >> + return false; >> + >> + if (!std_layout_type_p (derived)) >> + return false; > > I might move this check above derived_from_p, as it's just checking a flag. > >> + return pointer_interconvertible_base_of_p_1 (TYPE_BINFO (derived), >> base); >> +} >> + >> +/* 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) >> + return first_nonstatic_data_member_p (TREE_TYPE (field), >> + membertype); >> + if (first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) >> + return true; >> + } >> + else if (TREE_CODE (type) != UNION_TYPE) >> + return same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), >> + membertype); >> + else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE >> (field), >> + membertype)) >> + return true; > > Instead of checking !UNION_TYPE twice above, you could check it once > here and return false. > > More later. > >> + } >> + 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 (!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 +10788,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 +10918,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 +10936,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-28 12:06:00.482928952 +0200 >> +++ gcc/cp/cp-gimplify.c 2021-07-28 14:49:06.034027853 +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-21 18:55:05.416625466 +0200 >> +++ gcc/cp/tree.c 2021-07-28 14:07:41.181568566 +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-21 18:55:05.344626471 +0200 >> +++ gcc/cp/cxx-pretty-print.c 2021-07-28 12:14:20.675189115 +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-28 >> 13:21:41.654721641 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C >> 2021-07-28 13:21:20.562005151 +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-28 17:23:23.353247589 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-28 >> 17:36:10.514918765 +0200 >> @@ -0,0 +1,62 @@ >> +// 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 () {} }; >> + >> +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)); >> --- >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C.jj >> 2021-07-28 17:31:11.981938196 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-28 >> 17:39:45.458022541 +0200 >> @@ -0,0 +1,128 @@ >> +// 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 () {} }; >> + >> +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 (); >> +} >> --- >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C.jj >> 2021-07-28 17:31:19.863832080 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C 2021-07-28 >> 17:34:52.767965633 +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-28 17:36:30.412650659 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-28 >> 17:37:30.435841885 +0200 >> @@ -0,0 +1,24 @@ >> +// 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; }; >> + >> +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)); >> --- >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C.jj >> 2021-07-28 17:36:35.294584876 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-28 >> 17:39:08.795516549 +0200 >> @@ -0,0 +1,42 @@ >> +// 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; }; >> + >> +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 (); >> +} >> --- >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C.jj >> 2021-05-25 17:45:05.138850892 +0200 >> +++ >> gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C 2021-07-28 >> 17:53:43.244733852 +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 >> > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-29 20:38 ` [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jason Merrill 2021-07-30 5:41 ` Jason Merrill @ 2021-07-30 9:51 ` Jakub Jelinek 2021-07-30 15:00 ` Jason Merrill 1 sibling, 1 reply; 11+ messages in thread From: Jakub Jelinek @ 2021-07-30 9:51 UTC (permalink / raw) To: Jason Merrill; +Cc: Jonathan Wakely, gcc-patches 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 <jakub@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 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-30 9:51 ` Jakub Jelinek @ 2021-07-30 15:00 ` Jason Merrill 2021-07-30 15:23 ` Jakub Jelinek 0 siblings, 1 reply; 11+ messages in thread From: Jason Merrill @ 2021-07-30 15:00 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Jonathan Wakely, gcc-patches [-- Attachment #1: Type: text/plain, Size: 36225 bytes --] On 7/30/21 5:51 AM, Jakub Jelinek wrote: > On Thu, Jul 29, 2021 at 04:38:44PM -0400, Jason Merrill wrote: >> We don't already reject an anonymous struct with bases? I think we should >> do so, in fixup_anonymous_aggr. We might even require anonymous structs to >> be standard-layout. > > Not having base classes seems reasonable requirement for the anonymous > structures, after all, I couldn't find a way to refer to the members > in the base class - &Y::e is rejected with the above. Patch attached. > But standard layout means that even all the non-static members of the struct > need to be standard-layout, that seems an unnecessary requirement for > anon structures to me. Good point. But then, if the anonymous struct is non-standard-layout, that should make the enclosing class non-standard-layout as well, so we should never need to consider in the pointer-interconv code whether the anonymous struct is standard-layout. >>> +/* 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 <jakub@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; Here I was thinking just if (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 > [-- Attachment #2: anon-base.patch --] [-- Type: text/x-patch, Size: 594 bytes --] commit c4d6cfb788b2ca6676509b438955576583988d1d Author: Jason Merrill <jason@redhat.com> Date: Fri Jul 30 08:45:01 2021 -0400 anon-struct-base diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 01d64a16125..71308a06c63 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5084,6 +5084,9 @@ fixup_anonymous_aggr (tree t) { tree field, type; + if (BINFO_N_BASE_BINFOS (TYPE_BINFO (t))) + error_at (location_of (t), "anonymous struct with base classes"); + for (field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL) { ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-30 15:00 ` Jason Merrill @ 2021-07-30 15:23 ` Jakub Jelinek 2021-07-30 16:27 ` Jason Merrill 0 siblings, 1 reply; 11+ messages in thread From: Jakub Jelinek @ 2021-07-30 15:23 UTC (permalink / raw) To: Jason Merrill; +Cc: Jonathan Wakely, gcc-patches On Fri, Jul 30, 2021 at 11:00:26AM -0400, Jason Merrill wrote: > Patch attached. LGTM (which would mean I'll need to replace that particular test union with a different one which will have just a non-std-layout member of the anon struct, see below), but I guess we want a testcase for this, e.g. struct E { }; struct X { int a; struct : public E { short b; long c; }; long long d; }; union Y { int a; struct : public E { short b; long c; }; long long d; }; will do it. > > But standard layout means that even all the non-static members of the struct > > need to be standard-layout, that seems an unnecessary requirement for > > anon structures to me. > > Good point. > > But then, if the anonymous struct is non-standard-layout, that should make > the enclosing class non-standard-layout as well, so we should never need to > consider in the pointer-interconv code whether the anonymous struct is > standard-layout. For non-std-layout anon struct in a non-union class sure. But, for non-std-layout anon struct in a union, while it makes the union also non-std-layout, pointer-interconvertibility doesn't care about std-layout, even in non-std-layout unions address of each of the union member is pointer-interconvertible with the address of the whole union object. But when we recurse into the anon struct, the rule for non-union classes applies and it only handles std-layout. So something like: struct D { int x; private: int y; }; union Y { int a; struct { short b; long c; D z; }; long long 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)); D is not std-layout, therefore the anon-struct is not std-layout either and neither is union Y, for &Y::a and &Y::d the union pointer-interconvertibility rule applies and both are pointer-interconvertible, for the anon-struct it is not std-layout and therefore address of the artificial member with anon-struct type is not pointer-interconvertible with address of the b member. Jakub ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] 2021-07-30 15:23 ` Jakub Jelinek @ 2021-07-30 16:27 ` Jason Merrill 0 siblings, 0 replies; 11+ messages in thread From: Jason Merrill @ 2021-07-30 16:27 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Jonathan Wakely, gcc-patches On 7/30/21 11:23 AM, Jakub Jelinek wrote: > On Fri, Jul 30, 2021 at 11:00:26AM -0400, Jason Merrill wrote: >> Patch attached. > > LGTM (which would mean I'll need to replace that particular test union > with a different one which will have just a non-std-layout member of the > anon struct, see below), but I guess we want a testcase for this, e.g. > struct E { }; > struct X { int a; struct : public E { short b; long c; }; long long d; }; > union Y { int a; struct : public E { short b; long c; }; long long d; }; > will do it. I've now committed this change. >>> But standard layout means that even all the non-static members of the struct >>> need to be standard-layout, that seems an unnecessary requirement for >>> anon structures to me. >> >> Good point. >> >> But then, if the anonymous struct is non-standard-layout, that should make >> the enclosing class non-standard-layout as well, so we should never need to >> consider in the pointer-interconv code whether the anonymous struct is >> standard-layout. > > For non-std-layout anon struct in a non-union class sure. > But, for non-std-layout anon struct in a union, while it makes the union > also non-std-layout, pointer-interconvertibility doesn't care about > std-layout, even in non-std-layout unions address of each of the union member > is pointer-interconvertible with the address of the whole union object. Ah, right, I remembered that yesterday, but forgot this morning... So the handling in your revised patch is good; let's not worry about the case you mentioned with an anonymous struct member of the same type as another union member. Though > + if ((TREE_CODE (type) != UNION_TYPE This line could use either a comment or to be dropped; it's true that if type is non-union, we can skip the other checks because we know that any anonymous struct member must be standard-layout, but the other checks are cheap enough that I'm inclined to omit this one. OK either way and with the testcase adjustments for my patch. > + || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE > || std_layout_type_p (TREE_TYPE (field))) > && first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2021-07-30 16:27 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-07-29 7:50 [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jakub Jelinek 2021-07-29 14:21 ` [PATCH] c++: __builtin_is_pointer_interconvertible_with_class incremental fix [PR101539] Jakub Jelinek 2021-07-30 5:10 ` Jason Merrill 2021-07-30 7:57 ` Jakub Jelinek 2021-07-30 9:17 ` Jakub Jelinek 2021-07-29 20:38 ` [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539] Jason Merrill 2021-07-30 5:41 ` Jason Merrill 2021-07-30 9:51 ` Jakub Jelinek 2021-07-30 15:00 ` Jason Merrill 2021-07-30 15:23 ` Jakub Jelinek 2021-07-30 16:27 ` Jason Merrill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).