[PATCH] c++, v2: Implement P0466R5 __cpp_lib_is_layout_compatible compiler helpers [PR101539]

Jason Merrill jason@redhat.com
Mon Aug 16 19:57:21 GMT 2021


On 8/12/21 1:07 PM, Jakub Jelinek wrote:
> On Thu, Aug 12, 2021 at 12:06:33PM -0400, Jason Merrill wrote:
>> Yes; if the standard says something nonsensical, I prefer to figure out
>> something more sensible to propose as a change.
> 
> Ok, so here it is implemented, so far tested only on the new testcases
> (but nothing else really uses the code that has changed since the last
> patch).  Also attached incremental diff (so that it is also clear
> what test behaviors changed).
> I'll of course bootstrap/regtest it full tonight.
> 
> 2021-08-12  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/101539
> gcc/c-family/
> 	* c-common.h (enum rid): Add RID_IS_LAYOUT_COMPATIBLE.
> 	* c-common.c (c_common_reswords): Add __is_layout_compatible.
> gcc/cp/
> 	* cp-tree.h (enum cp_trait_kind): Add CPTK_IS_LAYOUT_COMPATIBLE.
> 	(enum cp_built_in_function): Add CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
> 	(fold_builtin_is_corresponding_member, layout_compatible_type_p):
> 	Declare.
> 	* parser.c (cp_parser_primary_expression): Handle
> 	RID_IS_LAYOUT_COMPATIBLE.
> 	(cp_parser_trait_expr): Likewise.
> 	* cp-objcp-common.c (names_builtin_p): Likewise.
> 	* constraint.cc (diagnose_trait_expr): Handle
> 	CPTK_IS_LAYOUT_COMPATIBLE.
> 	* decl.c (cxx_init_decl_processing): Register
> 	__builtin_is_corresponding_member builtin.
> 	* constexpr.c (cxx_eval_builtin_function_call): Handle
> 	CP_BUILT_IN_IS_CORRESPONDING_MEMBER builtin.
> 	* semantics.c (is_corresponding_member_union,
> 	is_corresponding_member_aggr, fold_builtin_is_corresponding_member):
> 	New functions.
> 	(trait_expr_value): Handle CPTK_IS_LAYOUT_COMPATIBLE.
> 	(finish_trait_expr): Likewise.
> 	* typeck.c (layout_compatible_type_p): New function.
> 	* cp-gimplify.c (cp_gimplify_expr): Fold
> 	CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
> 	(cp_fold): Likewise.
> 	* tree.c (builtin_valid_in_constant_expr_p): Handle
> 	CP_BUILT_IN_IS_CORRESPONDING_MEMBER.
> 	* cxx-pretty-print.c (pp_cxx_trait_expression): Handle
> 	CPTK_IS_LAYOUT_COMPATIBLE.
> 	* class.c (remove_zero_width_bit_fields): Remove.
> 	(layout_class_type): Don't call it.
> gcc/testsuite/
> 	* g++.dg/cpp2a/is-corresponding-member1.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member2.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member3.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member4.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member5.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member6.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member7.C: New test.
> 	* g++.dg/cpp2a/is-corresponding-member8.C: New test.
> 	* g++.dg/cpp2a/is-layout-compatible1.C: New test.
> 	* g++.dg/cpp2a/is-layout-compatible2.C: New test.
> 	* g++.dg/cpp2a/is-layout-compatible3.C: New test.
> 
> --- gcc/c-family/c-common.h.jj	2021-08-12 18:14:29.235853657 +0200
> +++ gcc/c-family/c-common.h	2021-08-12 18:21:01.141484689 +0200
> @@ -173,7 +173,8 @@ enum rid
>     RID_IS_ABSTRACT,             RID_IS_AGGREGATE,
>     RID_IS_BASE_OF,              RID_IS_CLASS,
>     RID_IS_EMPTY,                RID_IS_ENUM,
> -  RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
> +  RID_IS_FINAL,                RID_IS_LAYOUT_COMPATIBLE,
> +  RID_IS_LITERAL_TYPE,
>     RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
>     RID_IS_POD,                  RID_IS_POLYMORPHIC,
>     RID_IS_SAME_AS,
> --- gcc/c-family/c-common.c.jj	2021-08-03 00:44:32.762494219 +0200
> +++ gcc/c-family/c-common.c	2021-08-12 18:21:01.143484661 +0200
> @@ -420,6 +420,7 @@ const struct c_common_resword c_common_r
>     { "__is_empty",	RID_IS_EMPTY,	D_CXXONLY },
>     { "__is_enum",	RID_IS_ENUM,	D_CXXONLY },
>     { "__is_final",	RID_IS_FINAL,	D_CXXONLY },
> +  { "__is_layout_compatible", RID_IS_LAYOUT_COMPATIBLE, D_CXXONLY },
>     { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
>     { "__is_pointer_interconvertible_base_of",
>   			RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY },
> --- gcc/cp/cp-tree.h.jj	2021-08-12 09:34:16.817236456 +0200
> +++ gcc/cp/cp-tree.h	2021-08-12 18:21:01.144484647 +0200
> @@ -1365,6 +1365,7 @@ enum cp_trait_kind
>     CPTK_IS_EMPTY,
>     CPTK_IS_ENUM,
>     CPTK_IS_FINAL,
> +  CPTK_IS_LAYOUT_COMPATIBLE,
>     CPTK_IS_LITERAL_TYPE,
>     CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
>     CPTK_IS_POD,
> @@ -6358,6 +6359,7 @@ struct GTY((chain_next ("%h.next"))) tin
>   enum cp_built_in_function {
>     CP_BUILT_IN_IS_CONSTANT_EVALUATED,
>     CP_BUILT_IN_INTEGER_PACK,
> +  CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
>     CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
>     CP_BUILT_IN_SOURCE_LOCATION,
>     CP_BUILT_IN_LAST
> @@ -7574,6 +7576,7 @@ extern tree baselink_for_fns
>   extern void finish_static_assert                (tree, tree, location_t,
>   						 bool, bool);
>   extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
> +extern tree fold_builtin_is_corresponding_member (location_t, int, tree *);
>   extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *);
>   extern tree finish_trait_expr			(location_t, enum cp_trait_kind, tree, tree);
>   extern tree build_lambda_expr                   (void);
> @@ -7800,6 +7803,7 @@ extern bool comp_except_specs			(const_t
>   extern bool comptypes				(tree, tree, int);
>   extern bool same_type_ignoring_top_level_qualifiers_p (tree, tree);
>   extern bool similar_type_p			(tree, tree);
> +extern bool layout_compatible_type_p		(tree, tree);
>   extern bool compparms				(const_tree, const_tree);
>   extern int comp_cv_qualification		(const_tree, const_tree);
>   extern int comp_cv_qualification		(int, int);
> --- gcc/cp/parser.c.jj	2021-08-12 18:14:29.327852397 +0200
> +++ gcc/cp/parser.c	2021-08-12 18:21:01.148484592 +0200
> @@ -5816,6 +5816,7 @@ cp_parser_primary_expression (cp_parser
>   	case RID_IS_EMPTY:
>   	case RID_IS_ENUM:
>   	case RID_IS_FINAL:
> +	case RID_IS_LAYOUT_COMPATIBLE:
>   	case RID_IS_LITERAL_TYPE:
>   	case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
>   	case RID_IS_POD:
> @@ -10707,6 +10708,10 @@ cp_parser_trait_expr (cp_parser* parser,
>       case RID_IS_FINAL:
>         kind = CPTK_IS_FINAL;
>         break;
> +    case RID_IS_LAYOUT_COMPATIBLE:
> +      kind = CPTK_IS_LAYOUT_COMPATIBLE;
> +      binary = true;
> +      break;
>       case RID_IS_LITERAL_TYPE:
>         kind = CPTK_IS_LITERAL_TYPE;
>         break;
> --- gcc/cp/cp-objcp-common.c.jj	2021-08-03 00:44:32.835493200 +0200
> +++ gcc/cp/cp-objcp-common.c	2021-08-12 18:21:01.148484592 +0200
> @@ -413,6 +413,7 @@ names_builtin_p (const char *name)
>       case RID_IS_EMPTY:
>       case RID_IS_ENUM:
>       case RID_IS_FINAL:
> +    case RID_IS_LAYOUT_COMPATIBLE:
>       case RID_IS_LITERAL_TYPE:
>       case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
>       case RID_IS_POD:
> --- gcc/cp/constraint.cc.jj	2021-08-03 00:44:32.835493200 +0200
> +++ gcc/cp/constraint.cc	2021-08-12 18:21:01.149484579 +0200
> @@ -3628,6 +3628,9 @@ diagnose_trait_expr (tree expr, tree arg
>       case CPTK_IS_FINAL:
>         inform (loc, "  %qT is not a final class", t1);
>         break;
> +    case CPTK_IS_LAYOUT_COMPATIBLE:
> +      inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> +      break;
>       case CPTK_IS_LITERAL_TYPE:
>         inform (loc, "  %qT is not a literal type", t1);
>         break;
> --- gcc/cp/decl.c.jj	2021-08-11 23:43:59.170894072 +0200
> +++ gcc/cp/decl.c	2021-08-12 18:21:01.150484565 +0200
> @@ -4470,6 +4470,13 @@ cxx_init_decl_processing (void)
>     tree bool_vaftype = build_varargs_function_type_list (boolean_type_node,
>   							NULL_TREE);
>     decl
> +    = add_builtin_function ("__builtin_is_corresponding_member",
> +			    bool_vaftype,
> +			    CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
> +			    BUILT_IN_FRONTEND, NULL, NULL_TREE);
> +  set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
> +
> +  decl
>       = add_builtin_function ("__builtin_is_pointer_interconvertible_with_class",
>   			    bool_vaftype,
>   			    CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
> --- gcc/cp/constexpr.c.jj	2021-08-12 09:06:24.984783272 +0200
> +++ gcc/cp/constexpr.c	2021-08-12 18:21:01.151484551 +0200
> @@ -1438,6 +1438,18 @@ cxx_eval_builtin_function_call (const co
>   	= fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs,
>   							       args);
>       }
> +  else if (fndecl_built_in_p (fun,
> +			      CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
> +			      BUILT_IN_FRONTEND))
> +    {
> +      location_t loc = EXPR_LOCATION (t);
> +      if (nargs >= 2)
> +	{
> +	  VERIFY_CONSTANT (args[0]);
> +	  VERIFY_CONSTANT (args[1]);
> +	}
> +      new_call = fold_builtin_is_corresponding_member (loc, nargs, args);
> +    }
>     else
>       new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
>   					CALL_EXPR_FN (t), nargs, args);
> --- gcc/cp/semantics.c.jj	2021-08-12 18:14:29.356852000 +0200
> +++ gcc/cp/semantics.c	2021-08-12 18:43:25.333065804 +0200
> @@ -10670,6 +10670,292 @@ fold_builtin_is_pointer_inverconvertible
>   		      build_zero_cst (TREE_TYPE (arg)));
>   }
>   
> +/* Helper function for is_corresponding_member_aggr.  Return true if
> +   MEMBERTYPE pointer-to-data-member ARG can be found in anonymous
> +   union or structure BASETYPE.  */
> +
> +static bool
> +is_corresponding_member_union (tree basetype, tree membertype, tree arg)
> +{
> +  for (tree field = TYPE_FIELDS (basetype); field; field = DECL_CHAIN (field))
> +    if (TREE_CODE (field) != FIELD_DECL || DECL_BIT_FIELD_TYPE (field))
> +      continue;
> +    else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field),
> +							membertype))
> +      {
> +	if (TREE_CODE (arg) != INTEGER_CST
> +	    || tree_int_cst_equal (arg, byte_position (field)))
> +	  return true;
> +      }
> +    else if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
> +      {
> +	tree narg = arg;
> +	if (TREE_CODE (basetype) != UNION_TYPE
> +	    && TREE_CODE (narg) == INTEGER_CST)
> +	  narg = size_binop (MINUS_EXPR, arg, byte_position (field));
> +	if (is_corresponding_member_union (TREE_TYPE (field),
> +					   membertype, narg))
> +	  return true;
> +      }
> +  return false;
> +}
> +
> +/* Helper function for fold_builtin_is_corresponding_member call.
> +   Return boolean_false_node if MEMBERTYPE1 BASETYPE1::*ARG1 and
> +   MEMBERTYPE2 BASETYPE2::*ARG2 aren't corresponding members,
> +   boolean_true_node if they are corresponding members, or for
> +   non-constant ARG2 the highest member offset for corresponding
> +   members.  */
> +
> +static tree
> +is_corresponding_member_aggr (location_t loc, tree basetype1, tree membertype1,
> +			      tree arg1, tree basetype2, tree membertype2,
> +			      tree arg2)
> +{
> +  tree field1 = TYPE_FIELDS (basetype1);
> +  tree field2 = TYPE_FIELDS (basetype2);
> +  tree ret = boolean_false_node;
> +  while (1)
> +    {

Can we share more of the code between this function and 
layout_compatible_type_p?  I'm thinking of a function something like

bool next_common_initial_seqence (tree &mem1, tree &mem2)

that would update mem1/mem2 to the next entities of the common initial 
sequence (or null at the end) and return true, or return false if the 
next fields are not compatible.

> +      while (field1 && TREE_CODE (field1) != FIELD_DECL)
> +	field1 = DECL_CHAIN (field1);
> +      while (field2 && TREE_CODE (field2) != FIELD_DECL)
> +	field2 = DECL_CHAIN (field2);
> +      if (field1 && DECL_FIELD_IS_BASE (field1))
> +	{
> +	  if (is_empty_field (field1))
> +	    {
> +	      field1 = DECL_CHAIN (field1);
> +	      continue;
> +	    }
> +	  return is_corresponding_member_aggr (loc, TREE_TYPE (field1),
> +					       membertype1, arg1, basetype2,
> +					       membertype2, arg2);
> +	}
> +      if (field2 && DECL_FIELD_IS_BASE (field2))
> +	{
> +	  if (is_empty_field (field2))
> +	    {
> +	      field2 = DECL_CHAIN (field2);
> +	      continue;
> +	    }
> +	  return is_corresponding_member_aggr (loc, basetype1, membertype1,
> +					       arg1, TREE_TYPE (field2),
> +					       membertype2, arg2);
> +	}
> +      if (field1 == NULL_TREE || field2 == NULL_TREE)
> +	break;
> +      if ((!lookup_attribute ("no_unique_address",
> +			      DECL_ATTRIBUTES (field1)))
> +	  != !lookup_attribute ("no_unique_address",
> +				DECL_ATTRIBUTES (field2)))
> +	break;
> +      if (!tree_int_cst_equal (bit_position (field1),
> +			       bit_position (field2)))
> +	break;
> +      if (DECL_BIT_FIELD_TYPE (field1))
> +	{
> +	  if (!DECL_BIT_FIELD_TYPE (field2))
> +	    break;
> +	  if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (field1),
> +					 DECL_BIT_FIELD_TYPE (field2)))
> +	    break;
> +	  if (TYPE_PRECISION (TREE_TYPE (field1))
> +	      != TYPE_PRECISION (TREE_TYPE (field2)))
> +	    break;
> +	}
> +      else if (DECL_BIT_FIELD_TYPE (field2))
> +	break;
> +      else
> +	{
> +	  if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field1),
> +							 membertype1)
> +	      && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field2),
> +							    membertype2))
> +	    {
> +	      tree pos = byte_position (field1);
> +	      if (TREE_CODE (arg1) == INTEGER_CST
> +		  && tree_int_cst_equal (arg1, pos))
> +		{
> +		  if (TREE_CODE (arg2) == INTEGER_CST)
> +		    return boolean_true_node;
> +		  return pos;
> +		}
> +	      else if (TREE_CODE (arg1) != INTEGER_CST)
> +		ret = pos;
> +	    }
> +	  else if (ANON_AGGR_TYPE_P (TREE_TYPE (field1))
> +		   && ANON_AGGR_TYPE_P (TREE_TYPE (field2)))
> +	    {
> +	      bool overlap = true;
> +	      tree pos = byte_position (field1);
> +	      if (TREE_CODE (arg1) == INTEGER_CST)
> +		{
> +		  tree off1 = fold_convert (sizetype, arg1);
> +		  tree sz1 = TYPE_SIZE_UNIT (TREE_TYPE (field1));
> +		  if (tree_int_cst_lt (off1, pos)
> +		      || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz1),
> +					  off1))
> +		    overlap = false;
> +		}
> +	      if (TREE_CODE (arg2) == INTEGER_CST)
> +		{
> +		  tree off2 = fold_convert (sizetype, arg2);
> +		  tree sz2 = TYPE_SIZE_UNIT (TREE_TYPE (field2));
> +		  if (tree_int_cst_lt (off2, pos)
> +		      || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz2),
> +					  off2))
> +		    overlap = false;
> +		}
> +	      if (overlap
> +		  && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field1))
> +		  && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field2)))
> +		{
> +		  tree narg1 = arg1;
> +		  if (TREE_CODE (arg1) == INTEGER_CST)
> +		    narg1 = size_binop (MINUS_EXPR,
> +					fold_convert (sizetype, arg1), pos);
> +		  tree narg2 = arg2;
> +		  if (TREE_CODE (arg2) == INTEGER_CST)
> +		    narg2 = size_binop (MINUS_EXPR,
> +					fold_convert (sizetype, arg2), pos);
> +		  tree t1 = TREE_TYPE (field1);
> +		  tree t2 = TREE_TYPE (field2);
> +		  tree nret
> +		    = is_corresponding_member_aggr (loc, t1, membertype1,
> +						    narg1, t2, membertype2,
> +						    narg2);
> +		  if (nret != boolean_false_node)
> +		    {
> +		      if (nret == boolean_true_node)
> +			return nret;
> +		      if (TREE_CODE (arg1) == INTEGER_CST)
> +			return size_binop (PLUS_EXPR, nret, pos);
> +		      ret = size_binop (PLUS_EXPR, nret, pos);
> +		    }
> +		}
> +	      else if (overlap
> +		       && TREE_CODE (TREE_TYPE (field1)) == UNION_TYPE
> +		       && TREE_CODE (TREE_TYPE (field2)) == UNION_TYPE)
> +		{
> +		  tree narg1 = arg1;
> +		  if (TREE_CODE (arg1) == INTEGER_CST)
> +		    narg1 = size_binop (MINUS_EXPR,
> +					fold_convert (sizetype, arg1), pos);
> +		  tree narg2 = arg2;
> +		  if (TREE_CODE (arg2) == INTEGER_CST)
> +		    narg2 = size_binop (MINUS_EXPR,
> +					fold_convert (sizetype, arg2), pos);
> +		  if (is_corresponding_member_union (TREE_TYPE (field1),
> +						     membertype1, narg1)
> +		      && is_corresponding_member_union (TREE_TYPE (field2),
> +							membertype2, narg2))
> +		    {
> +		      sorry_at (loc, "%<__builtin_is_corresponding_member%> "
> +				     "not well defined for anonymous unions");
> +		      return boolean_false_node;
> +		    }
> +		}
> +	    }
> +	  if (!layout_compatible_type_p (TREE_TYPE (field1),
> +					 TREE_TYPE (field2)))
> +	    break;
> +	}
> +      field1 = DECL_CHAIN (field1);
> +      field2 = DECL_CHAIN (field2);
> +    }
> +  return ret;
> +}
> +
> +/* Fold __builtin_is_corresponding_member call.  */

> +tree
> +fold_builtin_is_corresponding_member (location_t loc, int nargs,
> +				      tree *args)
> +{
> +  /* Unless users call the builtin directly, the following 3 checks should be
> +     ensured from std::is_corresponding_member function template.  */
> +  if (nargs != 2)
> +    {
> +      error_at (loc, "%<__builtin_is_corresponding_member%> "
> +		     "needs two arguments");
> +      return boolean_false_node;
> +    }
> +  tree arg1 = args[0];
> +  tree arg2 = args[1];
> +  if (error_operand_p (arg1) || error_operand_p (arg2))
> +    return boolean_false_node;
> +  if (!TYPE_PTRMEM_P (TREE_TYPE (arg1))
> +      || !TYPE_PTRMEM_P (TREE_TYPE (arg2)))
> +    {
> +      error_at (loc, "%<__builtin_is_corresponding_member%> "
> +		     "argument is not pointer to member");
> +      return boolean_false_node;
> +    }
> +
> +  if (!TYPE_PTRDATAMEM_P (TREE_TYPE (arg1))
> +      || !TYPE_PTRDATAMEM_P (TREE_TYPE (arg2)))
> +    return boolean_false_node;
> +
> +  tree membertype1 = TREE_TYPE (TREE_TYPE (arg1));
> +  tree basetype1 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg1));
> +  if (!complete_type_or_else (basetype1, NULL_TREE))
> +    return boolean_false_node;
> +
> +  tree membertype2 = TREE_TYPE (TREE_TYPE (arg2));
> +  tree basetype2 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg2));
> +  if (!complete_type_or_else (basetype2, NULL_TREE))
> +    return boolean_false_node;
> +
> +  if (!NON_UNION_CLASS_TYPE_P (basetype1)
> +      || !NON_UNION_CLASS_TYPE_P (basetype2)
> +      || !std_layout_type_p (basetype1)
> +      || !std_layout_type_p (basetype2))
> +    return boolean_false_node;
> +
> +  /* If the member types aren't layout compatible, then they
> +     can't be corresponding members.  */
> +  if (!layout_compatible_type_p (membertype1, membertype2))
> +    return boolean_false_node;
> +
> +  if (TREE_CODE (arg1) == PTRMEM_CST)
> +    arg1 = cplus_expand_constant (arg1);
> +  if (TREE_CODE (arg2) == PTRMEM_CST)
> +    arg2 = cplus_expand_constant (arg2);
> +
> +  if (null_member_pointer_value_p (arg1)
> +      || null_member_pointer_value_p (arg2))
> +    return boolean_false_node;
> +
> +  if (TREE_CODE (arg1) == INTEGER_CST
> +      && TREE_CODE (arg2) == INTEGER_CST
> +      && !tree_int_cst_equal (arg1, arg2))
> +    return boolean_false_node;
> +
> +  if (TREE_CODE (arg2) == INTEGER_CST
> +      && TREE_CODE (arg1) != INTEGER_CST)
> +    {
> +      std::swap (arg1, arg2);
> +      std::swap (membertype1, membertype2);
> +      std::swap (basetype1, basetype2);
> +    }
> +
> +  tree ret = is_corresponding_member_aggr (loc, basetype1, membertype1, arg1,
> +					   basetype2, membertype2, arg2);
> +  if (TREE_TYPE (ret) == boolean_type_node)
> +    return ret;
> +  gcc_assert (TREE_CODE (arg2) != INTEGER_CST);
> +  if (TREE_CODE (arg1) == INTEGER_CST)
> +    return fold_build2 (EQ_EXPR, boolean_type_node, arg1,
> +			fold_convert (TREE_TYPE (arg1), arg2));

Add a comment here to explain that the <= operation below is checking 
that arg1 is within the common initial sequence.

> +  ret = fold_build2 (LE_EXPR, boolean_type_node,
> +		     fold_convert (pointer_sized_int_node, arg1),
> +		     fold_convert (pointer_sized_int_node, ret));
> +  return fold_build2 (TRUTH_AND_EXPR, boolean_type_node, ret,
> +		      fold_build2 (EQ_EXPR, boolean_type_node, arg1,
> +				   fold_convert (TREE_TYPE (arg1), arg2)));
> +}
> +
>   /* Actually evaluates the trait.  */
>   
>   static bool
> @@ -10760,6 +11046,9 @@ trait_expr_value (cp_trait_kind kind, tr
>       case CPTK_IS_FINAL:
>         return CLASS_TYPE_P (type1) && CLASSTYPE_FINAL (type1);
>   
> +    case CPTK_IS_LAYOUT_COMPATIBLE:
> +      return layout_compatible_type_p (type1, type2);
> +
>       case CPTK_IS_LITERAL_TYPE:
>         return literal_type_p (type1);
>   
> @@ -10907,6 +11196,19 @@ finish_trait_expr (location_t loc, cp_tr
>       case CPTK_IS_SAME_AS:
>         break;
>   
> +    case CPTK_IS_LAYOUT_COMPATIBLE:
> +      if (!array_of_unknown_bound_p (type1)
> +	  && TREE_CODE (type1) != VOID_TYPE
> +	  && !complete_type_or_else (type1, NULL_TREE))
> +	/* We already issued an error.  */
> +	return error_mark_node;
> +      if (!array_of_unknown_bound_p (type2)
> +	  && TREE_CODE (type2) != VOID_TYPE
> +	  && !complete_type_or_else (type2, NULL_TREE))
> +	/* We already issued an error.  */
> +	return error_mark_node;
> +      break;
> +
>       default:
>         gcc_unreachable ();
>       }
> --- gcc/cp/typeck.c.jj	2021-08-03 00:44:32.960491457 +0200
> +++ gcc/cp/typeck.c	2021-08-12 18:31:32.404835518 +0200
> @@ -1621,6 +1621,145 @@ similar_type_p (tree type1, tree type2)
>     return false;
>   }
>   
> +/* Return true if TYPE1 and TYPE2 are layout-compatible types.  */
> +
> +bool
> +layout_compatible_type_p (tree type1, tree type2)
> +{
> +  if (type1 == error_mark_node || type2 == error_mark_node)
> +    return false;
> +  if (type1 == type2)
> +    return true;
> +  if (TREE_CODE (type1) != TREE_CODE (type2))
> +    return false;
> +
> +  type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> +  type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> +
> +  if (TREE_CODE (type1) == ENUMERAL_TYPE)
> +    return (TYPE_ALIGN (type1) == TYPE_ALIGN (type2)
> +	    && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))
> +	    && same_type_p (finish_underlying_type (type1),
> +			    finish_underlying_type (type2)));
> +
> +  if (CLASS_TYPE_P (type1)
> +      && std_layout_type_p (type1)
> +      && std_layout_type_p (type2)
> +      && TYPE_ALIGN (type1) == TYPE_ALIGN (type2)
> +      && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2)))
> +    {
> +      tree field1 = TYPE_FIELDS (type1);
> +      tree field2 = TYPE_FIELDS (type2);
> +      if (TREE_CODE (type1) == RECORD_TYPE)
> +	{
> +	  while (1)
> +	    {
> +	      while (field1 && TREE_CODE (field1) != FIELD_DECL)
> +		field1 = DECL_CHAIN (field1);
> +	      while (field2 && TREE_CODE (field2) != FIELD_DECL)
> +		field2 = DECL_CHAIN (field2);
> +	      if (field1 && DECL_FIELD_IS_BASE (field1))
> +		{
> +		  if (is_empty_field (field1))
> +		    {
> +		      field1 = DECL_CHAIN (field1);
> +		      continue;
> +		    }
> +		  return layout_compatible_type_p (TREE_TYPE (field1), type2);
> +		}
> +	      if (field2 && DECL_FIELD_IS_BASE (field2))
> +		{
> +		  if (is_empty_field (field2))
> +		    {
> +		      field2 = DECL_CHAIN (field2);
> +		      continue;
> +		    }
> +		  return layout_compatible_type_p (type1, TREE_TYPE (field2));
> +		}
> +	      if (field1 == NULL_TREE && field2 == NULL_TREE)
> +		return true;
> +	      if (field1 == NULL_TREE || field2 == NULL_TREE)
> +		return false;
> +	      if (DECL_BIT_FIELD_TYPE (field1))
> +		{
> +		  if (!DECL_BIT_FIELD_TYPE (field2))
> +		    return false;
> +		  if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (field1),
> +						 DECL_BIT_FIELD_TYPE (field2)))
> +		    return false;
> +		  if (TYPE_PRECISION (TREE_TYPE (field1))
> +		      != TYPE_PRECISION (TREE_TYPE (field2)))
> +		    return false;
> +		}
> +	      else if (DECL_BIT_FIELD_TYPE (field2))
> +		return false;
> +	      else if (!layout_compatible_type_p (TREE_TYPE (field1),
> +						  TREE_TYPE (field2)))
> +		return false;
> +	      if ((!lookup_attribute ("no_unique_address",
> +				      DECL_ATTRIBUTES (field1)))
> +		  != !lookup_attribute ("no_unique_address",
> +					DECL_ATTRIBUTES (field2)))
> +		return false;
> +	      if (!tree_int_cst_equal (bit_position (field1),
> +				       bit_position (field2)))
> +		return false;
> +	      field1 = DECL_CHAIN (field1);
> +	      field2 = DECL_CHAIN (field2);
> +	    }
> +	}

Add a comment that the code below is for the union case, and include the 
layout-compatibility definition for unions.

> +      auto_vec<tree, 16> vec;
> +      unsigned int count = 0;
> +      for (; field1; field1 = DECL_CHAIN (field1))
> +	if (TREE_CODE (field1) == FIELD_DECL)
> +	  count++;
> +      for (; field2; field2 = DECL_CHAIN (field2))
> +	if (TREE_CODE (field2) == FIELD_DECL)
> +	  vec.safe_push (field2);
> +      if (count != vec.length ())
> +	return false;

Add a comment that discussion in core suggests that we might move toward 
treating multiple union fields of the same type as the same field, so 
this constraint might get dropped in the future.

> +      for (field1 = TYPE_FIELDS (type1); field1; field1 = DECL_CHAIN (field1))
> +	{
> +	  if (TREE_CODE (field1) != FIELD_DECL)
> +	    continue;
> +	  unsigned int j;
> +	  tree t1 = DECL_BIT_FIELD_TYPE (field1);
> +	  if (t1 == NULL_TREE)
> +	    t1 = TREE_TYPE (field1);
> +	  FOR_EACH_VEC_ELT (vec, j, field2)
> +	    {
> +	      tree t2 = DECL_BIT_FIELD_TYPE (field2);
> +	      if (t2 == NULL_TREE)
> +		t2 = TREE_TYPE (field2);
> +	      if (DECL_BIT_FIELD_TYPE (field1))
> +		{
> +		  if (!DECL_BIT_FIELD_TYPE (field2))
> +		    continue;
> +		  if (TYPE_PRECISION (TREE_TYPE (field1))
> +		      != TYPE_PRECISION (TREE_TYPE (field2)))
> +		    continue;
> +		}
> +	      else if (DECL_BIT_FIELD_TYPE (field2))
> +		continue;
> +	      if (!layout_compatible_type_p (t1, t2))
> +		continue;
> +	      if ((!lookup_attribute ("no_unique_address",
> +				      DECL_ATTRIBUTES (field1)))
> +		  != !lookup_attribute ("no_unique_address",
> +					DECL_ATTRIBUTES (field2)))
> +		continue;
> +	      break;
> +	    }
> +	  if (j == vec.length ())
> +	    return false;
> +	  vec.unordered_remove (j);

If the above constraint is dropped, we will want to stop removing j here 
as well.

> +	}
> +      return true;
> +    }
> +
> +  return same_type_p (type1, type2);
> +}
> +
>   /* Returns 1 if TYPE1 is at least as qualified as TYPE2.  */
>   
>   bool
> --- gcc/cp/cp-gimplify.c.jj	2021-08-03 00:44:32.835493200 +0200
> +++ gcc/cp/cp-gimplify.c	2021-08-12 18:21:01.153484524 +0200
> @@ -658,12 +658,20 @@ cp_gimplify_expr (tree *expr_p, gimple_s
>   		*expr_p
>   		  = fold_builtin_source_location (EXPR_LOCATION (*expr_p));
>   		break;
> +	      case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
> +		*expr_p
> +		  = fold_builtin_is_corresponding_member
> +			(EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
> +			 &CALL_EXPR_ARG (*expr_p, 0));
> +		break;
>   	      case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
>   		*expr_p
>   		  = fold_builtin_is_pointer_inverconvertible_with_class
>   			(EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
>   			 &CALL_EXPR_ARG (*expr_p, 0));
>   		break;
> +	      default:
> +		break;
>   	      }
>   	}
>         break;
> @@ -2579,6 +2587,11 @@ cp_fold (tree x)
>   	      case CP_BUILT_IN_SOURCE_LOCATION:
>   		x = fold_builtin_source_location (EXPR_LOCATION (x));
>   		break;
> +	      case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
> +	        x = fold_builtin_is_corresponding_member
> +			(EXPR_LOCATION (x), call_expr_nargs (x),
> +			 &CALL_EXPR_ARG (x, 0));
> +		break;
>   	      case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
>                   x = fold_builtin_is_pointer_inverconvertible_with_class
>   			(EXPR_LOCATION (x), call_expr_nargs (x),
> --- gcc/cp/tree.c.jj	2021-08-11 23:43:59.193893755 +0200
> +++ gcc/cp/tree.c	2021-08-12 18:21:01.154484510 +0200
> @@ -455,6 +455,7 @@ builtin_valid_in_constant_expr_p (const_
>   	  {
>   	  case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
>   	  case CP_BUILT_IN_SOURCE_LOCATION:
> +	  case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
>   	  case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
>   	    return true;
>   	  default:
> --- gcc/cp/cxx-pretty-print.c.jj	2021-08-03 00:44:32.837493173 +0200
> +++ gcc/cp/cxx-pretty-print.c	2021-08-12 18:21:01.154484510 +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_LAYOUT_COMPATIBLE:
> +      pp_cxx_ws_string (pp, "__is_layout_compatible");
> +      break;
>       case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
>         pp_cxx_ws_string (pp, "__is_pointer_interconvertible_base_of");
>         break;
> @@ -2700,6 +2703,7 @@ pp_cxx_trait_expression (cxx_pretty_prin
>   
>     if (kind == CPTK_IS_BASE_OF
>         || kind == CPTK_IS_SAME_AS
> +      || kind == CPTK_IS_LAYOUT_COMPATIBLE
>         || kind == CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF)
>       {
>         pp_cxx_separate_with (pp, ',');
> --- gcc/cp/class.c.jj	2021-08-03 00:44:32.833493228 +0200
> +++ gcc/cp/class.c	2021-08-12 18:21:01.155484496 +0200
> @@ -136,7 +136,6 @@ static bool check_field_decl (tree, tree
>   static void check_field_decls (tree, tree *, int *, int *);
>   static void build_base_fields (record_layout_info, splay_tree, tree *);
>   static void check_methods (tree);
> -static void remove_zero_width_bit_fields (tree);
>   static bool accessible_nvdtor_p (tree);
>   
>   /* Used by find_flexarrays and related functions.  */
> @@ -5754,31 +5753,6 @@ type_build_dtor_call (tree t)
>     return false;
>   }
>   
> -/* Remove all zero-width bit-fields from T.  */
> -
> -static void
> -remove_zero_width_bit_fields (tree t)
> -{
> -  tree *fieldsp;
> -
> -  fieldsp = &TYPE_FIELDS (t);
> -  while (*fieldsp)
> -    {
> -      if (TREE_CODE (*fieldsp) == FIELD_DECL
> -	  && DECL_C_BIT_FIELD (*fieldsp)
> -	  /* We should not be confused by the fact that grokbitfield
> -	     temporarily sets the width of the bit field into
> -	     DECL_BIT_FIELD_REPRESENTATIVE (*fieldsp).
> -	     check_bitfield_decl eventually sets DECL_SIZE (*fieldsp)
> -	     to that width.  */
> -	  && (DECL_SIZE (*fieldsp) == NULL_TREE
> -	      || integer_zerop (DECL_SIZE (*fieldsp))))
> -	*fieldsp = DECL_CHAIN (*fieldsp);
> -      else
> -	fieldsp = &DECL_CHAIN (*fieldsp);
> -    }
> -}
> -
>   /* Returns TRUE iff we need a cookie when dynamically allocating an
>      array whose elements have the indicated class TYPE.  */
>   
> @@ -6770,10 +6744,6 @@ layout_class_type (tree t, tree *virtual
>         normalize_rli (rli);
>       }
>   
> -  /* Delete all zero-width bit-fields from the list of fields.  Now
> -     that the type is laid out they are no longer important.  */
> -  remove_zero_width_bit_fields (t);
> -
>     if (CLASSTYPE_NON_LAYOUT_POD_P (t) || CLASSTYPE_EMPTY_P (t))
>       {
>         /* T needs a different layout as a base (eliding virtual bases
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member1.C	2021-08-12 18:21:01.155484496 +0200
> @@ -0,0 +1,61 @@
> +// P0466R5
> +// { dg-do compile { target c++20 } }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +}
> +
> +struct A { int a; };
> +struct B { const int b; };
> +struct C { int a; unsigned int b; int f; A c; int : 0; int d; double e; };
> +struct D { const int x; unsigned int y; int g; B z; int u; double w; };
> +struct E { int a; [[no_unique_address]] int b; };
> +struct F { int c; const int d; };
> +struct G { double a; int b; double c; };
> +struct H { const volatile double d; int e : 16; double f; };
> +struct I { const double g; int h : 15; const double i; };
> +struct J : public A {};
> +struct K {};
> +struct L : public K, public B {};
> +union U { int a; };
> +struct V { void foo () {}; };
> +struct W { int a; private: int b; public: int c; };
> +struct Z : public A, public B {};
> +
> +static_assert (std::is_corresponding_member (&A::a, &A::a));
> +static_assert (std::is_corresponding_member (&A::a, &B::b));
> +static_assert (std::is_corresponding_member (&C::a, &D::x));
> +static_assert (std::is_corresponding_member (&C::b, &D::y));
> +static_assert (std::is_corresponding_member (&C::f, &D::g));
> +static_assert (std::is_corresponding_member (&C::c, &D::z));
> +static_assert (!std::is_corresponding_member (&C::d, &D::u));
> +static_assert (!std::is_corresponding_member (&C::e, &D::w));
> +static_assert (!std::is_corresponding_member (&C::f, &D::x));
> +static_assert (!std::is_corresponding_member (&C::a, &D::g));
> +static_assert (std::is_corresponding_member (&E::a, &F::c));
> +static_assert (!std::is_corresponding_member (&E::b, &F::d));
> +static_assert (std::is_corresponding_member (&G::a, &H::d));
> +static_assert (!std::is_corresponding_member (&G::c, &H::f));
> +static_assert (std::is_corresponding_member (&H::d, &I::g));
> +static_assert (!std::is_corresponding_member (&H::f, &I::i));
> +static_assert (std::is_corresponding_member (&J::a, &B::b));
> +static_assert (std::is_corresponding_member<J, B, int, const int> (&J::a, &B::b));
> +static_assert (std::is_corresponding_member (&J::a, &L::b));
> +static_assert (std::is_corresponding_member<J, L, int, const int> (&J::a, &L::b));
> +static_assert (std::is_corresponding_member (&L::b, &B::b));
> +static_assert (std::is_corresponding_member<L, B, const int, const int> (&L::b, &B::b));
> +static_assert (!std::is_corresponding_member (&U::a, &U::a));
> +static_assert (!std::is_corresponding_member (&A::a, (int A::*) nullptr));
> +static_assert (!std::is_corresponding_member ((int A::*) nullptr, &A::a));
> +static_assert (!std::is_corresponding_member ((int A::*) nullptr, (int A::*) nullptr));
> +static_assert (!std::is_corresponding_member (&V::foo, &V::foo));
> +static_assert (!std::is_corresponding_member (&W::a, &W::a));
> +static_assert (!std::is_corresponding_member (&W::c, &W::c));
> +static_assert (std::is_corresponding_member (&Z::a, &Z::b));
> +static_assert (!std::is_corresponding_member<Z, Z, int, const int> (&Z::a, &Z::b));
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member2.C	2021-08-12 18:21:01.155484496 +0200
> @@ -0,0 +1,158 @@
> +// P0466R5
> +// { dg-do run { target c++20 } }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +}
> +
> +struct A { int a; };
> +struct B { const int b; };
> +struct C { int a; unsigned int b; int f; A c; int : 0; int d; double e; };
> +struct D { const int x; unsigned int y; int g; B z; int u; double w; };
> +struct E { int a; [[no_unique_address]] int b; };
> +struct F { int c; const int d; };
> +struct G { double a; int b; double c; };
> +struct H { const volatile double d; int e : 16; double f; };
> +struct I { const double g; int h : 15; const double i; };
> +struct J : public A {};
> +struct K {};
> +struct L : public K, public B {};
> +union U { int a; };
> +struct V { void foo () {}; };
> +struct W { int a; private: int b; public: int c; };
> +struct Z : public A, public B {};
> +
> +int
> +main ()
> +{
> +  auto t1 = &A::a;
> +  auto t2 = &A::a;
> +  if (!std::is_corresponding_member (t1, t2))
> +    __builtin_abort ();
> +  auto t3 = &A::a;
> +  auto t4 = &B::b;
> +  if (!std::is_corresponding_member (t3, t4))
> +    __builtin_abort ();
> +  auto t5 = &C::a;
> +  auto t6 = &D::x;
> +  if (!std::is_corresponding_member (t5, t6))
> +    __builtin_abort ();
> +  auto t9 = &C::b;
> +  auto t10 = &D::y;
> +  if (!std::is_corresponding_member (t9, t10))
> +    __builtin_abort ();
> +  auto t11 = &C::f;
> +  auto t12 = &D::g;
> +  if (!std::is_corresponding_member (t11, t12))
> +    __builtin_abort ();
> +  auto t13 = &C::c;
> +  auto t14 = &D::z;
> +  if (!std::is_corresponding_member (t13, t14))
> +    __builtin_abort ();
> +  auto t15 = &C::d;
> +  auto t16 = &D::u;
> +  if (std::is_corresponding_member (t15, t16))
> +    __builtin_abort ();
> +  auto t17 = &C::e;
> +  auto t18 = &D::w;
> +  if (std::is_corresponding_member (t17, t18))
> +    __builtin_abort ();
> +  auto t19 = &C::f;
> +  auto t20 = &D::x;
> +  if (std::is_corresponding_member (t19, t20))
> +    __builtin_abort ();
> +  auto t21 = &C::a;
> +  auto t22 = &D::g;
> +  if (std::is_corresponding_member (t21, t22))
> +    __builtin_abort ();
> +  auto t23 = &E::a;
> +  auto t24 = &F::c;
> +  if (!std::is_corresponding_member (t23, t24))
> +    __builtin_abort ();
> +  auto t25 = &E::b;
> +  auto t26 = &F::d;
> +  if (std::is_corresponding_member (t25, t26))
> +    __builtin_abort ();
> +  auto t27 = &G::a;
> +  auto t28 = &H::d;
> +  if (!std::is_corresponding_member (t27, t28))
> +    __builtin_abort ();
> +  auto t29 = &G::c;
> +  auto t30 = &H::f;
> +  if (std::is_corresponding_member (t29, t30))
> +    __builtin_abort ();
> +  auto t31 = &H::d;
> +  auto t32 = &I::g;
> +  if (!std::is_corresponding_member (t31, t32))
> +    __builtin_abort ();
> +  auto t33 = &H::f;
> +  auto t34 = &I::i;
> +  if (std::is_corresponding_member (t33, t34))
> +    __builtin_abort ();
> +  auto t35 = &J::a;
> +  auto t36 = &B::b;
> +  if (!std::is_corresponding_member (t35, t36))
> +    __builtin_abort ();
> +  int J::*t37 = &J::a;
> +  const int B::*t38 = &B::b;
> +  if (!std::is_corresponding_member (t37, t38))
> +    __builtin_abort ();
> +  auto t39 = &J::a;
> +  auto t40 = &L::b;
> +  if (!std::is_corresponding_member (t39, t40))
> +    __builtin_abort ();
> +  int J::*t41 = &J::a;
> +  const int L::*t42 = &L::b;
> +  if (!std::is_corresponding_member (t41, t42))
> +    __builtin_abort ();
> +  auto t43 = &L::b;
> +  auto t44 = &B::b;
> +  if (!std::is_corresponding_member (t43, t44))
> +    __builtin_abort ();
> +  const int L::*t45 = &L::b;
> +  const int B::*t46 = &B::b;
> +  if (!std::is_corresponding_member (t45, t46))
> +    __builtin_abort ();
> +  auto t47 = &U::a;
> +  auto t48 = &U::a;
> +  if (std::is_corresponding_member (t47, t48))
> +    __builtin_abort ();
> +  auto t49 = &A::a;
> +  auto t50 = (int A::*) nullptr;
> +  if (std::is_corresponding_member (t49, t50))
> +    __builtin_abort ();
> +  auto t51 = (int A::*) nullptr;
> +  auto t52 = &A::a;
> +  if (std::is_corresponding_member (t51, t52))
> +    __builtin_abort ();
> +  auto t53 = (int A::*) nullptr;
> +  auto t54 = (int A::*) nullptr;
> +  if (std::is_corresponding_member (t53, t54))
> +    __builtin_abort ();
> +  auto t55 = &V::foo;
> +  auto t56 = &V::foo;
> +  if (std::is_corresponding_member (t55, t56))
> +    __builtin_abort ();
> +  auto t57 = &W::a;
> +  auto t58 = &W::a;
> +  if (std::is_corresponding_member (t57, t58))
> +    __builtin_abort ();
> +  auto t59 = &W::c;
> +  auto t60 = &W::c;
> +  if (std::is_corresponding_member (t59, t60))
> +    __builtin_abort ();
> +  auto t61 = &Z::a;
> +  auto t62 = &Z::b;
> +  if (!std::is_corresponding_member (t61, t62))
> +    __builtin_abort ();
> +  int Z::*t63 = &Z::a;
> +  const int Z::*t64 = &Z::b;
> +  if (std::is_corresponding_member (t63, t64))
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member3.C	2021-08-12 18:21:01.155484496 +0200
> @@ -0,0 +1,14 @@
> +// P0466R5
> +// { dg-do compile { target c++20 } }
> +
> +struct A { int a; };
> +struct B;
> +
> +bool a = __builtin_is_corresponding_member ();			// { dg-error "needs two arguments" }
> +bool b = __builtin_is_corresponding_member (&A::a);		// { dg-error "needs two arguments" }
> +bool c = __builtin_is_corresponding_member (&A::a, &A::a, &A::a);	// { dg-error "needs two arguments" }
> +bool d = __builtin_is_corresponding_member (&A::a, 1);			// { dg-error "argument is not pointer to member" }
> +bool e = __builtin_is_corresponding_member (1.0, &A::a);		// { dg-error "argument is not pointer to member" }
> +bool f = __builtin_is_corresponding_member (1, A{});		// { dg-error "argument is not pointer to member" }
> +bool g = __builtin_is_corresponding_member (&A::a, (int B::*) nullptr);	// { dg-error "invalid use of incomplete type" }
> +bool h = __builtin_is_corresponding_member ((int B::*) nullptr, &A::a);	// { dg-error "invalid use of incomplete type" }
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C	2021-08-12 18:21:01.155484496 +0200
> @@ -0,0 +1,25 @@
> +// P0466R5
> +// { dg-do compile { target c++20 } }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);	// { dg-error "invalid use of incomplete type 'struct B'" }
> +}
> +}
> +
> +struct A { int a; };
> +struct B;
> +constexpr int B::*n = nullptr;
> +constexpr auto a = std::is_corresponding_member (&A::a, n);	// { dg-error "invalid use of incomplete type 'struct B'" }
> +constexpr auto b = std::is_corresponding_member (n, &A::a);	// { dg-error "invalid use of incomplete type 'struct B'" }
> +
> +void
> +foo (int B::*m)
> +{
> +  std::is_corresponding_member (&A::a, m);
> +  std::is_corresponding_member (m, &A::a);
> +}
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member5.C	2021-08-12 18:53:12.607012566 +0200
> @@ -0,0 +1,95 @@
> +// P0466R5
> +// { dg-do run { target c++20 } }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +}
> +
> +struct S {};
> +struct T {};
> +struct I { int a; };
> +struct alignas(16) J { const int b; };
> +struct K { char b; char s[15]; I c; short d; };
> +struct L { char d; char t[15]; J e; short f; };
> +struct U { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; [[no_unique_address]] S a3; short a4; };
> +struct V { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; [[no_unique_address]] S b3; short b4; };
> +struct U1 { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; [[no_unique_address]] S a3; short a4; };
> +struct V1 { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; [[no_unique_address]] S b3; short b4; };
> +struct A { int a; union { short b; long c; }; int d; signed char e; int f; };
> +struct B { const int a; union { signed long b; short c; }; volatile int d; unsigned char e; int f; };
> +struct A1 { int a; union { short b; long c; }; int d; short e; int f; };
> +struct B1 { const int a; union { signed long b; short c; }; volatile int d; unsigned short e; int f; };
> +
> +static_assert (std::is_corresponding_member (&I::a, &J::b));
> +static_assert (std::is_corresponding_member (&K::b, &L::d));
> +static_assert (!std::is_corresponding_member (&K::c, &L::e));
> +static_assert (std::is_corresponding_member (&U::a0, &V::b0));
> +static_assert (!std::is_corresponding_member (&U::a4, &V::b4));
> +static_assert (std::is_corresponding_member (&A::a, &B::a));
> +static_assert (std::is_corresponding_member (&A::d, &B::d));
> +static_assert (!std::is_corresponding_member (&A::e, &B::e));
> +static_assert (!std::is_corresponding_member (&A::f, &B::f));
> +static_assert (!std::is_corresponding_member (&A::a, &B::f));
> +static_assert (!std::is_corresponding_member (&A::d, &B::a));
> +static_assert (!std::is_corresponding_member (&A::a, &B::d));
> +static_assert (!std::is_corresponding_member (&A::f, &B::a));
> +static_assert (!std::is_corresponding_member (&A1::e, &B1::e));
> +
> +int
> +main ()
> +{
> +  auto t1 = &I::a;
> +  auto t2 = &J::b;
> +  if (!std::is_corresponding_member (t1, t2))
> +    __builtin_abort ();
> +  auto t3 = &K::b;
> +  auto t4 = &L::d;
> +  if (!std::is_corresponding_member (t3, t4))
> +    __builtin_abort ();
> +  auto t5 = &K::c;
> +  auto t6 = &L::e;
> +  if (std::is_corresponding_member (t5, t6))
> +    __builtin_abort ();
> +  auto t7 = &U::a0;
> +  auto t8 = &V::b0;
> +  if (!std::is_corresponding_member (t7, t8))
> +    __builtin_abort ();
> +  auto t9 = &U::a4;
> +  auto t10 = &V::b4;
> +  if (std::is_corresponding_member (t9, t10))
> +    __builtin_abort ();
> +  auto t11 = &A::a;
> +  auto t12 = &B::a;
> +  auto t13 = &A::d;
> +  auto t14 = &B::d;
> +  auto t15 = &A::e;
> +  auto t16 = &B::e;
> +  auto t17 = &A::f;
> +  auto t18 = &B::f;
> +  if (!std::is_corresponding_member (t11, t12))
> +    __builtin_abort ();
> +  if (!std::is_corresponding_member (t13, t14))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t15, t16))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t17, t18))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t11, t18))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t13, t12))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t11, t14))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t17, t12))
> +    __builtin_abort ();
> +  auto t19 = &A1::e;
> +  auto t20 = &B1::e;
> +  if (std::is_corresponding_member (t19, t20))
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member6.C	2021-08-12 18:56:22.440409391 +0200
> @@ -0,0 +1,34 @@
> +// P0466R5
> +// { dg-do compile { target c++20 } }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +// { dg-message "'__builtin_is_corresponding_member' not well defined for anonymous unions" "" { target *-*-* } .-2 }
> +}
> +
> +struct S {};
> +struct T {};
> +struct I { int a; };
> +struct alignas(16) J { const int b; };
> +struct K { char b; char s[15]; alignas(16) I c; short d; };
> +struct L { char d; char t[15]; J e; short f; };
> +struct U { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; [[no_unique_address]] S a3; short a4; };
> +struct V { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; [[no_unique_address]] S b3; short b4; };
> +struct U1 { int a0; [[no_unique_address]] S a1; [[no_unique_address]] S a2; [[no_unique_address]] S a3; short a4; };
> +struct V1 { int b0; [[no_unique_address]] S b1; [[no_unique_address]] T b2; [[no_unique_address]] S b3; short b4; };
> +struct A { int a; union { short b; long c; }; int d; signed char e; int f; };
> +struct B { const int a; union { signed long b; short c; }; volatile int d; unsigned char e; int f; };
> +
> +static_assert (!std::is_corresponding_member (&K::d, &L::f));
> +static_assert (std::is_corresponding_member (&U::a1, &V::b1));
> +static_assert (!std::is_corresponding_member (&U::a2, &V::b2));
> +static_assert (!std::is_corresponding_member (&U::a3, &V::b3));
> +static_assert (!std::is_corresponding_member (&U1::a3, &V1::b3));
> +static_assert (!std::is_corresponding_member (&A::b, &B::c));
> +constexpr auto a = std::is_corresponding_member (&A::c, &B::b);		// { dg-message "required from here" }
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C.jj	2021-08-12 18:21:01.155484496 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member7.C	2021-08-12 18:21:01.155484496 +0200
> @@ -0,0 +1,71 @@
> +// P0466R5
> +// { dg-do run { target c++20 } }
> +// { dg-options "" }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +}
> +
> +struct A { int a; struct { int b; short c; long d; }; int : 0; int e; };
> +struct B { const signed int a; struct { int b; signed short c; signed long d; }; volatile int e; };
> +struct C { int a; union { struct { short b; long c; }; long d; short e; }; signed int f; };
> +struct D { int a; union { long b; short c; struct { short d; signed long e; }; }; int f; };
> +
> +static_assert (std::is_corresponding_member (&A::a, &B::a));
> +static_assert (std::is_corresponding_member (&A::b, &B::b));
> +static_assert (std::is_corresponding_member (&A::c, &B::c));
> +static_assert (std::is_corresponding_member (&A::d, &B::d));
> +static_assert (!std::is_corresponding_member (&A::e, &B::e));
> +static_assert (!std::is_corresponding_member (&A::a, &B::b));
> +static_assert (!std::is_corresponding_member (&A::b, &B::a));
> +static_assert (std::is_corresponding_member (&C::a, &D::a));
> +static_assert (std::is_corresponding_member (&C::f, &D::f));
> +static_assert (!std::is_corresponding_member (&C::a, &D::f));
> +static_assert (!std::is_corresponding_member (&C::f, &D::a));
> +
> +int
> +main ()
> +{
> +  auto t1 = &A::a;
> +  auto t2 = &B::a;
> +  auto t3 = &A::b;
> +  auto t4 = &B::b;
> +  auto t5 = &A::c;
> +  auto t6 = &B::c;
> +  auto t7 = &A::d;
> +  auto t8 = &B::d;
> +  auto t9 = &A::e;
> +  auto t10 = &B::e;
> +  if (!std::is_corresponding_member (t1, t2))
> +    __builtin_abort ();
> +  if (!std::is_corresponding_member (t3, t4))
> +    __builtin_abort ();
> +  if (!std::is_corresponding_member (t5, t6))
> +    __builtin_abort ();
> +  if (!std::is_corresponding_member (t7, t8))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t9, t10))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t1, t4))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t3, t2))
> +    __builtin_abort ();
> +  auto t11 = &C::a;
> +  auto t12 = &D::a;
> +  auto t13 = &C::f;
> +  auto t14 = &D::f;
> +  if (!std::is_corresponding_member (t11, t12))
> +    __builtin_abort ();
> +  if (!std::is_corresponding_member (t13, t14))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t11, t14))
> +    __builtin_abort ();
> +  if (std::is_corresponding_member (t13, t12))
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C.jj	2021-08-12 18:21:01.156484483 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-corresponding-member8.C	2021-08-12 18:58:35.669582429 +0200
> @@ -0,0 +1,25 @@
> +// P0466R5
> +// { dg-do compile { target c++20 } }
> +// { dg-options "" }
> +
> +namespace std
> +{
> +template <class S1, class S2, class M1, class M2>
> +constexpr bool
> +is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> +{
> +  return __builtin_is_corresponding_member (m1, m2);
> +}
> +// { dg-message "'__builtin_is_corresponding_member' not well defined for anonymous unions" "" { target *-*-* } .-2 }
> +}
> +
> +struct A { int a; struct { short b; short c; long d; }; int : 0; int e; };
> +struct B { const signed int a; struct alignas(16) { short b; signed short c; signed long d; }; volatile int e; };
> +struct C { int a; union { struct { int b; long c; }; long d; short e; }; signed int f; };
> +struct D { int a; union { long b; short c; struct { int d; signed long e; }; }; int f; };
> +
> +static_assert (std::is_corresponding_member (&A::a, &B::a));
> +static_assert (!std::is_corresponding_member (&A::b, &B::b));
> +static_assert (!std::is_corresponding_member (&A::c, &B::c));
> +static_assert (!std::is_corresponding_member (&A::d, &B::d));
> +auto a = std::is_corresponding_member (&C::a, &D::a);		// { dg-message "required from here" }
> --- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C.jj	2021-08-12 18:21:01.156484483 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible1.C	2021-08-12 18:21:01.156484483 +0200
> @@ -0,0 +1,80 @@
> +// 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_layout_compatible;
> +
> +template<typename T, typename U>
> +struct is_layout_compatible
> +  : public integral_constant <bool, __is_layout_compatible (T, U)>
> +{
> +};
> +
> +template <typename T, typename U>
> +inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
> +}
> +
> +struct A { int a; char b; };
> +struct B { const int c; volatile char d; };
> +struct C { int a : 1; int : 7; int : 0; int b : 2; };
> +struct D { int : 1; int c : 7; int : 0; int : 2; };
> +struct E { int f : 1; int : 7; int g : 2; };
> +struct F { int a; signed char b; };
> +union G { int a; long long b; signed char c; unsigned char d; int e; };
> +union H { long long f; unsigned char g; int h; int i; signed char j; };
> +struct I : public A {};
> +struct J {};
> +struct K : public J {};
> +struct L {};
> +struct M : public K, L { const int a; volatile char b; };
> +struct N {};
> +struct O : public N, M {};
> +struct P { int a; private: int b; public: int c; };
> +struct Q { int a; private: int b; public: int c; };
> +union U1 { int a; private: int b; public: int c; };
> +union U2 { int a; private: int b; public: int c; };
> +struct S {};
> +struct T {};
> +struct W;
> +struct X;
> +enum E1 : int { E11, E12 };
> +enum E2 : int { E21, E22 };
> +enum E3 : long { E31, E32 };
> +enum E4 { E41, E42 };
> +enum E5 { E51, E52 };
> +
> +static_assert (std::is_layout_compatible<int, const int>::value);
> +static_assert (std::is_layout_compatible_v<double, volatile double>);
> +static_assert (std::is_layout_compatible_v<A, B>);
> +static_assert (std::is_layout_compatible_v<C, D>);
> +static_assert (!std::is_layout_compatible_v<int, unsigned int>);
> +static_assert (!std::is_layout_compatible_v<A, F>);
> +static_assert (std::is_layout_compatible_v<G, H>);
> +static_assert (std::is_layout_compatible_v<S, T>);
> +static_assert (std::is_layout_compatible_v<A[3], A[3]>);
> +static_assert (std::is_layout_compatible_v<A[], A[]>);
> +static_assert (!std::is_layout_compatible_v<S[1], T[1]>);
> +static_assert (std::is_layout_compatible_v<W[], W[]>);
> +static_assert (!std::is_layout_compatible_v<W[], X[]>);
> +static_assert (!std::is_layout_compatible_v<D, E>);
> +static_assert (std::is_layout_compatible_v<void, const void>);
> +static_assert (std::is_layout_compatible_v<I, const A>);
> +static_assert (std::is_layout_compatible_v<volatile A, const I>);
> +static_assert (std::is_layout_compatible_v<M, A>);
> +static_assert (std::is_layout_compatible_v<O, M>);
> +static_assert (std::is_layout_compatible_v<A, O>);
> +static_assert (std::is_layout_compatible_v<P, P>);
> +static_assert (!std::is_layout_compatible_v<P, Q>);
> +static_assert (std::is_layout_compatible_v<U1, U1>);
> +static_assert (!std::is_layout_compatible_v<U1, U2>);
> +static_assert (std::is_layout_compatible_v<E1, E2>);
> +static_assert (!std::is_layout_compatible_v<E1, E3>);
> +static_assert (std::is_layout_compatible_v<E4, E5>);
> --- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C.jj	2021-08-12 18:21:01.156484483 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible2.C	2021-08-12 18:21:01.156484483 +0200
> @@ -0,0 +1,36 @@
> +// 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_layout_compatible;
> +
> +template<typename T, typename U>
> +struct is_layout_compatible
> +  : public integral_constant <bool, __is_layout_compatible (T, U)>
> +{
> +};
> +
> +template <typename T, typename U>
> +inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
> +}
> +// { dg-error "invalid use of incomplete type 'struct W'" "" { target *-*-* } .-2 }
> +// { dg-error "invalid use of incomplete type 'struct \[XY]'" "" { target *-*-* } .-3 }
> +// { dg-error "invalid use of incomplete type 'struct Z'" "" { target *-*-* } .-4 }
> +
> +struct W;
> +struct X;
> +struct Y;
> +struct Z;
> +struct A {};
> +
> +auto a = std::is_layout_compatible_v<W, W>;
> +auto b = std::is_layout_compatible_v<X, Y>;
> +auto c = std::is_layout_compatible_v<A, Z>;
> --- gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C.jj	2021-08-12 18:21:01.156484483 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/is-layout-compatible3.C	2021-08-12 18:50:42.424072011 +0200
> @@ -0,0 +1,59 @@
> +// 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_layout_compatible;
> +
> +template<typename T, typename U>
> +struct is_layout_compatible
> +  : public integral_constant <bool, __is_layout_compatible (T, U)>
> +{
> +};
> +
> +template <typename T, typename U>
> +inline constexpr bool is_layout_compatible_v = __is_layout_compatible (T, U);
> +}
> +
> +// Weird cases.
> +struct S {};
> +struct T {};
> +struct I { int a; };
> +struct alignas(16) J { const int b; };
> +struct K { I c; int d; };
> +struct L { J e; int f; };
> +union M { I u; };
> +union N { J v; };
> +union O { int a; int b; };
> +union P { int a : 1; int b : 12; };
> +enum Q : int { Q1, Q2 };
> +enum alignas(16) R : int { R1, R2 };
> +struct U { [[no_unique_address]] S a1; [[no_unique_address]] S a2; [[no_unique_address]] S a3; };
> +struct V { [[no_unique_address]] S b1; [[no_unique_address]] T b2; [[no_unique_address]] S b3; };
> +struct alignas(16) A : public I {};
> +struct alignas(16) B {};
> +struct C : public B, public I {};
> +union D { int a : 3; int b : 9; };
> +struct alignas(16) E { alignas(16) int a; alignas(16) int b; };
> +struct alignas(16) F { int c; alignas(16) int d; };
> +union alignas(16) G { int a; alignas(16) short b; };
> +union alignas(16) H { short c; int d; };
> +
> +static_assert (!std::is_layout_compatible_v<I, J>);
> +static_assert (!std::is_layout_compatible_v<K, L>);
> +static_assert (!std::is_layout_compatible_v<M, N>);
> +static_assert (!std::is_layout_compatible_v<O, P>);
> +static_assert (!std::is_layout_compatible_v<P, D>);
> +static_assert (!std::is_layout_compatible_v<Q, R>);
> +static_assert (!std::is_layout_compatible_v<U, V>);
> +static_assert (!std::is_layout_compatible_v<A, I>);
> +static_assert (!std::is_layout_compatible_v<C, I>);
> +static_assert (std::is_layout_compatible_v<E, F>);
> +static_assert (std::is_layout_compatible_v<G, H>);
> 
> 
> 	Jakub
> 



More information about the Gcc-patches mailing list