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