From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: Jonathan Wakely <jwakely@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539]
Date: Fri, 30 Jul 2021 11:00:26 -0400 [thread overview]
Message-ID: <f7a020c7-f5c5-0539-b55e-45569aebb951@redhat.com> (raw)
In-Reply-To: <20210730095151.GH2380545@tucnak>
[-- 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)
{
next prev parent reply other threads:[~2021-07-30 15:00 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
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-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 [this message]
2021-07-30 15:23 ` Jakub Jelinek
2021-07-30 16:27 ` Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f7a020c7-f5c5-0539-b55e-45569aebb951@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=jwakely@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).