https://gcc.gnu.org/g:154b21d46be6c920a4743cf8dc0e0bbaedf55409
commit r16-5522-g154b21d46be6c920a4743cf8dc0e0bbaedf55409 Author: Nathaniel Shead <[email protected]> Date: Sat Oct 25 22:40:06 2025 +1100 c++: Add detailed diagnostics for __is_pointer_interconvertible_base_of gcc/cp/ChangeLog: * constraint.cc (diagnose_trait_expr): <case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF>: Explain failure with more detail. * cp-tree.h (pointer_interconvertible_base_of_p): Add explain parameter. * semantics.cc (pointer_interconvertible_base_of_p): Explain why not. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/is-pointer-interconvertible-base-of2.C: New test. Signed-off-by: Nathaniel Shead <[email protected]> Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/constraint.cc | 3 +- gcc/cp/cp-tree.h | 1 + gcc/cp/semantics.cc | 47 ++++++++++++++++++---- .../cpp2a/is-pointer-interconvertible-base-of2.C | 29 +++++++++++++ 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 19d2d8f82d51..6abd0966fcd1 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3239,8 +3239,9 @@ diagnose_trait_expr (location_t loc, tree expr, tree args) break; case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF: inform (location_of (t2), - "%qT is not a pointer-interconvertible base of %qT", + "%qT is not a pointer-interconvertible base of %qT, because", t1, t2); + pointer_interconvertible_base_of_p (t1, t2, /*explain=*/true); break; case CPTK_IS_POD: inform (loc, "%qT is not a POD type", t1); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5244370f7c6c..4efddd98537d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8291,6 +8291,7 @@ extern void finish_static_assert (tree, tree, location_t, bool, bool, bool = false); extern tree finish_decltype_type (tree, bool, tsubst_flags_t); extern tree fold_builtin_is_corresponding_member (location_t, int, tree *); +extern bool pointer_interconvertible_base_of_p (tree, tree, bool = false); extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *); extern tree finish_structured_binding_size (location_t, tree, tsubst_flags_t); extern tree finish_trait_expr (location_t, enum cp_trait_kind, tree, tree); diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 0d8981ecabf9..f472be21c4bb 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -13088,26 +13088,57 @@ 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. */ +/* Return true if BASE is a pointer-interconvertible base of DERIVED. */ -static bool -pointer_interconvertible_base_of_p (tree base, tree derived) +bool +pointer_interconvertible_base_of_p (tree base, tree derived, + bool explain/*=false*/) { 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 (!NON_UNION_CLASS_TYPE_P (base)) + { + if (explain) + inform (location_of (base), + "%qT is not a non-union class type", base); + return false; + } + if (!NON_UNION_CLASS_TYPE_P (derived)) + { + if (explain) + inform (location_of (derived), + "%qT is not a non-union class type", derived); + return false; + } if (same_type_p (base, derived)) return true; if (!std_layout_type_p (derived)) - return false; + { + if (explain) + inform (location_of (derived), + "%qT is not a standard-layout type", derived); + return false; + } + + if (!uniquely_derived_from_p (base, derived)) + { + if (explain) + { + /* An ambiguous base should already be impossible due to + the std_layout_type_p check. */ + gcc_checking_assert (!DERIVED_FROM_P (base, derived)); + inform (location_of (derived), + "%qT is not a base of %qT", base, derived); + } + return false; + } - return uniquely_derived_from_p (base, derived); + return true; } /* Helper function for fold_builtin_is_pointer_inverconvertible_with_class, diff --git a/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of2.C b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of2.C new file mode 100644 index 000000000000..9530f9efe8a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of2.C @@ -0,0 +1,29 @@ +// { dg-do compile { target c++20 } } +// { dg-additional-options "-Wno-inaccessible-base" } + +template <typename T, typename U> +constexpr bool pointer_interconvertible_base_of + = __is_pointer_interconvertible_base_of(T, U); + +struct A {}; +struct B {}; // { dg-message "not a pointer-interconvertible base" } +static_assert(pointer_interconvertible_base_of<A, B>); // { dg-error "assert" } + +struct C { + int x; +private: + int y; +}; +struct D : C {}; // { dg-message "standard-layout" } +static_assert(pointer_interconvertible_base_of<C, D>); // { dg-error "assert" } + +struct E {}; +struct F : E {}; +struct G : F, E {}; // { dg-message "standard-layout" } +static_assert(pointer_interconvertible_base_of<E, G>); // { dg-error "assert" } + +union H {}; // { dg-message "non-union" } +static_assert(pointer_interconvertible_base_of<H, H>); // { dg-error "assert" } + +static_assert(pointer_interconvertible_base_of<A, int>); // { dg-error "assert" } +// { dg-message "non-union" "" { target *-*-* } .-1 }
