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  <ja...@redhat.com>

        PR c++/101539
gcc/c-family/
        * c-common.h (enum rid): Add RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
        * c-common.c (c_common_reswords): Add
        __is_pointer_interconvertible_base_of.
gcc/cp/
        * cp-tree.h (enum cp_trait_kind): Add
        CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
        (enum cp_built_in_function): Add
        CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS.
        (fold_builtin_is_pointer_inverconvertible_with_class): Declare.
        * parser.c (cp_parser_primary_expression): Handle
        RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
        (cp_parser_trait_expr): Likewise.
        * cp-objcp-common.c (names_builtin_p): Likewise.
        * constraint.cc (diagnose_trait_expr): Handle
        CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
        * decl.c (cxx_init_decl_processing): Register
        __builtin_is_pointer_interconvertible_with_class builtin.
        * constexpr.c (cxx_eval_builtin_function_call): Handle
        CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS builtin.
        * semantics.c (pointer_interconvertible_base_of_p,
        first_nonstatic_data_member_p,
        fold_builtin_is_pointer_inverconvertible_with_class): New functions.
        (trait_expr_value): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
        (finish_trait_expr): Likewise.  Formatting fix.
        * cp-gimplify.c (cp_gimplify_expr): Fold
        CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS.  Call
        fndecl_built_in_p just once.
        (cp_fold): Likewise.
        * tree.c (builtin_valid_in_constant_expr_p): Handle
        CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS.  Call
        fndecl_built_in_p just once.
        * cxx-pretty-print.c (pp_cxx_trait_expression): Handle
        CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF.
gcc/testsuite/
        * g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C: New test.
        * g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C: New test.

--- gcc/c-family/c-common.h.jj  2021-07-29 13:24:08.368481637 +0200
+++ gcc/c-family/c-common.h     2021-07-30 11:19:39.391615252 +0200
@@ -174,6 +174,7 @@ enum rid
    RID_IS_BASE_OF,              RID_IS_CLASS,
    RID_IS_EMPTY,                RID_IS_ENUM,
    RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
+  RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
    RID_IS_POD,                  RID_IS_POLYMORPHIC,
    RID_IS_SAME_AS,
    RID_IS_STD_LAYOUT,           RID_IS_TRIVIAL,
--- gcc/c-family/c-common.c.jj  2021-07-29 13:24:42.667013302 +0200
+++ gcc/c-family/c-common.c     2021-07-30 11:19:39.411614978 +0200
@@ -421,6 +421,8 @@ const struct c_common_resword c_common_r
    { "__is_enum",    RID_IS_ENUM,    D_CXXONLY },
    { "__is_final",   RID_IS_FINAL,   D_CXXONLY },
    { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
+  { "__is_pointer_interconvertible_base_of",
+                       RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY },
    { "__is_pod",             RID_IS_POD,     D_CXXONLY },
    { "__is_polymorphic",     RID_IS_POLYMORPHIC, D_CXXONLY },
    { "__is_same",     RID_IS_SAME_AS, D_CXXONLY },
--- gcc/cp/cp-tree.h.jj 2021-07-29 13:24:08.673477473 +0200
+++ gcc/cp/cp-tree.h    2021-07-30 11:19:39.426614772 +0200
@@ -1366,6 +1366,7 @@ enum cp_trait_kind
    CPTK_IS_ENUM,
    CPTK_IS_FINAL,
    CPTK_IS_LITERAL_TYPE,
+  CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
    CPTK_IS_POD,
    CPTK_IS_POLYMORPHIC,
    CPTK_IS_SAME_AS,
@@ -6355,6 +6356,7 @@ struct GTY((chain_next ("%h.next"))) tin
  enum cp_built_in_function {
    CP_BUILT_IN_IS_CONSTANT_EVALUATED,
    CP_BUILT_IN_INTEGER_PACK,
+  CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
    CP_BUILT_IN_SOURCE_LOCATION,
    CP_BUILT_IN_LAST
  };
@@ -7570,6 +7572,7 @@ extern tree baselink_for_fns
  extern void finish_static_assert                (tree, tree, location_t,
                                                 bool, bool);
  extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
+extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, 
int, tree *);
  extern tree finish_trait_expr                 (location_t, enum 
cp_trait_kind, tree, tree);
  extern tree build_lambda_expr                   (void);
  extern tree build_lambda_object                       (tree);
--- gcc/cp/parser.c.jj  2021-07-30 10:30:07.115413053 +0200
+++ gcc/cp/parser.c     2021-07-30 11:19:39.431614703 +0200
@@ -5799,6 +5799,7 @@ cp_parser_primary_expression (cp_parser
        case RID_IS_ENUM:
        case RID_IS_FINAL:
        case RID_IS_LITERAL_TYPE:
+       case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
        case RID_IS_POD:
        case RID_IS_POLYMORPHIC:
        case RID_IS_SAME_AS:
@@ -10688,6 +10689,10 @@ cp_parser_trait_expr (cp_parser* parser,
      case RID_IS_LITERAL_TYPE:
        kind = CPTK_IS_LITERAL_TYPE;
        break;
+    case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
+      kind = CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF;
+      binary = true;
+      break;
      case RID_IS_POD:
        kind = CPTK_IS_POD;
        break;
--- gcc/cp/cp-objcp-common.c.jj 2021-07-29 13:24:08.599478483 +0200
+++ gcc/cp/cp-objcp-common.c    2021-07-30 11:19:39.440614580 +0200
@@ -414,6 +414,7 @@ names_builtin_p (const char *name)
      case RID_IS_ENUM:
      case RID_IS_FINAL:
      case RID_IS_LITERAL_TYPE:
+    case RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
      case RID_IS_POD:
      case RID_IS_POLYMORPHIC:
      case RID_IS_SAME_AS:
--- gcc/cp/constraint.cc.jj     2021-07-29 13:24:08.527479466 +0200
+++ gcc/cp/constraint.cc        2021-07-30 11:19:39.455614374 +0200
@@ -3631,6 +3631,10 @@ diagnose_trait_expr (tree expr, tree arg
      case CPTK_IS_LITERAL_TYPE:
        inform (loc, "  %qT is not a literal type", t1);
        break;
+    case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
+      inform (loc, "  %qT is not pointer-interconvertible base of %qT",
+             t1, t2);
+      break;
      case CPTK_IS_POD:
        inform (loc, "  %qT is not a POD type", t1);
        break;
--- gcc/cp/decl.c.jj    2021-07-29 13:24:08.797475780 +0200
+++ gcc/cp/decl.c       2021-07-30 11:19:39.476614087 +0200
@@ -4467,6 +4467,15 @@ cxx_init_decl_processing (void)
                               BUILT_IN_FRONTEND, NULL, NULL_TREE);
    set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+ tree bool_vaftype = build_varargs_function_type_list (boolean_type_node,
+                                                       NULL_TREE);
+  decl
+    = add_builtin_function ("__builtin_is_pointer_interconvertible_with_class",
+                           bool_vaftype,
+                           CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
+                           BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+
    integer_two_node = build_int_cst (NULL_TREE, 2);
/* Guess at the initial static decls size. */
--- gcc/cp/constexpr.c.jj       2021-07-29 13:24:08.447480558 +0200
+++ gcc/cp/constexpr.c  2021-07-30 11:19:39.493613854 +0200
@@ -1427,8 +1427,20 @@ cxx_eval_builtin_function_call (const co
        && ctx->call
        && ctx->call->fundef)
      current_function_decl = ctx->call->fundef->decl;
-  new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
-                                     CALL_EXPR_FN (t), nargs, args);
+  if (fndecl_built_in_p (fun,
+                        CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
+                        BUILT_IN_FRONTEND))
+    {
+      location_t loc = EXPR_LOCATION (t);
+      if (nargs >= 1)
+       VERIFY_CONSTANT (args[0]);
+      new_call
+       = fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs,
+                                                              args);
+    }
+  else
+    new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
+                                       CALL_EXPR_FN (t), nargs, args);
    current_function_decl = save_cur_fn;
    force_folding_builtin_constant_p = save_ffbcp;
    if (new_call == NULL)
--- gcc/cp/semantics.c.jj       2021-07-29 13:24:08.960473554 +0200
+++ gcc/cp/semantics.c  2021-07-30 11:27:16.247352685 +0200
@@ -10566,6 +10566,111 @@ classtype_has_nothrow_assign_or_copy_p (
    return saw_copy;
  }
+/* Return true if DERIVED is pointer interconvertible base of BASE. */
+
+static bool
+pointer_interconvertible_base_of_p (tree base, tree derived)
+{
+  if (base == error_mark_node || derived == error_mark_node)
+    return false;
+  base = TYPE_MAIN_VARIANT (base);
+  derived = TYPE_MAIN_VARIANT (derived);
+  if (!NON_UNION_CLASS_TYPE_P (base)
+      || !NON_UNION_CLASS_TYPE_P (derived))
+    return false;
+
+  if (same_type_p (base, derived))
+    return true;
+
+  if (!std_layout_type_p (derived))
+    return false;
+
+  return uniquely_derived_from_p (base, derived);
+}
+
+/* Helper function for fold_builtin_is_pointer_inverconvertible_with_class,
+   return true if MEMBERTYPE is the type of the first non-static data member
+   of TYPE or for unions of any members.  */
+static bool
+first_nonstatic_data_member_p (tree type, tree membertype)
+{
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
+      if (DECL_FIELD_IS_BASE (field) && is_empty_field (field))
+       continue;
+      if (DECL_FIELD_IS_BASE (field))
+       return first_nonstatic_data_member_p (TREE_TYPE (field), membertype);
+      if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
+       {
+         if ((TREE_CODE (type) != UNION_TYPE
+              || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE
+              || std_layout_type_p (TREE_TYPE (field)))
+             && first_nonstatic_data_member_p (TREE_TYPE (field), membertype))
+           return true;

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


commit c4d6cfb788b2ca6676509b438955576583988d1d
Author: Jason Merrill <ja...@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 via email to