public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
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)
 	  {

  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).