diff options
-rw-r--r-- | gcc/c-family/c-common.c | 2 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 1 | ||||
-rw-r--r-- | gcc/cp/constexpr.c | 16 | ||||
-rw-r--r-- | gcc/cp/constraint.cc | 4 | ||||
-rw-r--r-- | gcc/cp/cp-gimplify.c | 50 | ||||
-rw-r--r-- | gcc/cp/cp-objcp-common.c | 1 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/cxx-pretty-print.c | 7 | ||||
-rw-r--r-- | gcc/cp/decl.c | 9 | ||||
-rw-r--r-- | gcc/cp/parser.c | 5 | ||||
-rw-r--r-- | gcc/cp/semantics.c | 114 | ||||
-rw-r--r-- | gcc/cp/tree.c | 15 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C | 55 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C | 65 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C | 135 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C | 11 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C | 31 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C | 59 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C | 19 |
19 files changed, 578 insertions, 24 deletions
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 21da679..00ac3c5 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -421,6 +421,8 @@ const struct c_common_resword c_common_reswords[] = { "__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 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index c4b2789..65d8c1c 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -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, diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 31fa5b6..1af365d 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1427,8 +1427,20 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, && 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) diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 4ee5215..e608c5a 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3631,6 +3631,10 @@ diagnose_trait_expr (tree expr, tree args) 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; diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 0520fa4..6e274ac 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -648,14 +648,23 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) 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, diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index ee25573..beef012 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -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: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ddf8f43..9a47a87 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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"))) tinst_level { 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 (tree); 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); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 3709d0f..b899162 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -2645,6 +2645,9 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t) 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_printer *pp, tree t) 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)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 71308a0..e4be6be 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 97078f9..ab74e9d 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -5799,6 +5799,7 @@ cp_parser_primary_expression (cp_parser *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, enum rid keyword) 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; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index f64b084..34e5d76 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -10566,6 +10566,110 @@ classtype_has_nothrow_assign_or_copy_p (tree type, bool assign_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 (TREE_TYPE (field)) == UNION_TYPE + || std_layout_type_p (TREE_TYPE (field))) + && 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 +10763,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) 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 +10893,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) 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 +10911,9 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) 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, diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 2a14fa9..8345396 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -450,11 +450,16 @@ builtin_valid_in_constant_expr_p (const_tree decl) 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; } diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C new file mode 100644 index 0000000..d94f18f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C @@ -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>); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C new file mode 100644 index 0000000..efce355 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C @@ -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)); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C new file mode 100644 index 0000000..b663a39 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C new file mode 100644 index 0000000..b02f1b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C new file mode 100644 index 0000000..e5e2451 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C @@ -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 { int x; private: int y; }; +union Y { int a; struct { short b; long c; D z; }; 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::z)); +static_assert (std::is_pointer_interconvertible_with_class (&Y::d)); diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C new file mode 100644 index 0000000..246ff92 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C @@ -0,0 +1,59 @@ +// 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 { int x; private: int y; }; +union Y { int a; struct { short b; long c; D z; }; 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::z; + if (std::is_pointer_interconvertible_with_class (t11)) + __builtin_abort (); + auto t12 = &Y::d; + if (!std::is_pointer_interconvertible_with_class (t12)) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C new file mode 100644 index 0000000..4b6801c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C @@ -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); |