Now that I'm writing the above text and rereading the
pointer-interconvertibility definition, I think my
first_nonstatic_data_member_p
and fold_builtin_is_pointer_inverconvertible_with_class have one bug,
for unions the pointer inter-convertibility doesn't talk about std
layout at
all, so I think I need to check for std_layout_type_p only for non-union
class types and accept any union, std_layout_type_p or not. But when
recursing from a union type into anonymous structure type punt if the
anonymous structure type is not std_layout_type_p + add testcase
coverage.
Anyway, the patch has been bootstrapped/regtested on x86_64-linux and
i686-linux.
2021-07-29 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_1,
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-21 18:55:05.239627937 +0200
+++ gcc/c-family/c-common.h 2021-07-28 12:07:31.717700448 +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-21 18:55:05.239627937 +0200
+++ gcc/c-family/c-common.c 2021-07-28 12:08:26.419963865 +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-21 18:55:05.326626722 +0200
+++ gcc/cp/cp-tree.h 2021-07-28 14:08:55.920562235 +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-27 09:47:44.098780350 +0200
+++ gcc/cp/parser.c 2021-07-28 12:10:24.669370137 +0200
@@ -5797,6 +5797,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:
@@ -10686,6 +10687,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-21 18:55:05.301627072 +0200
+++ gcc/cp/cp-objcp-common.c 2021-07-28 12:10:40.358158672 +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-21 18:55:05.281627351 +0200
+++ gcc/cp/constraint.cc 2021-07-28 12:12:31.875655576 +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-21 18:55:05.362626220 +0200
+++ gcc/cp/decl.c 2021-07-28 13:35:55.640239697 +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-21 18:55:05.256627699 +0200
+++ gcc/cp/constexpr.c 2021-07-28 17:12:01.796428596 +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-22 12:37:20.450532703 +0200
+++ gcc/cp/semantics.c 2021-07-28 16:51:05.119363473 +0200
@@ -10566,6 +10566,135 @@ classtype_has_nothrow_assign_or_copy_p (
return saw_copy;
}
+/* 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;
+ 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 +10788,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 +10918,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 +10936,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-28 12:06:00.482928952 +0200
+++ gcc/cp/cp-gimplify.c 2021-07-28 14:49:06.034027853 +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-21 18:55:05.416625466 +0200
+++ gcc/cp/tree.c 2021-07-28 14:07:41.181568566 +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-21 18:55:05.344626471 +0200
+++ gcc/cp/cxx-pretty-print.c 2021-07-28 12:14:20.675189115 +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-28
13:21:41.654721641 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C
2021-07-28 13:21:20.562005151 +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-28 17:23:23.353247589 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C 2021-07-28
17:36:10.514918765 +0200
@@ -0,0 +1,62 @@
+// 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 () {} };
+
+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));
---
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C.jj
2021-07-28 17:31:11.981938196 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C 2021-07-28
17:39:45.458022541 +0200
@@ -0,0 +1,128 @@
+// 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 () {} };
+
+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 ();
+}
---
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C.jj
2021-07-28 17:31:19.863832080 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C 2021-07-28
17:34:52.767965633 +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-28 17:36:30.412650659 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C 2021-07-28
17:37:30.435841885 +0200
@@ -0,0 +1,24 @@
+// 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; };
+
+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));
---
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C.jj
2021-07-28 17:36:35.294584876 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C 2021-07-28
17:39:08.795516549 +0200
@@ -0,0 +1,42 @@
+// 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; };
+
+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 ();
+}
---
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C.jj
2021-05-25 17:45:05.138850892 +0200
+++
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C 2021-07-28
17:53:43.244733852 +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