OK for trunk? I didn't realise before implementing that this basically
just ends up being a 'std_layout_type_p' + 'is_base_of' check, so it
hardly seems worth it given that I haven't worked out a good way to
explain the former yet, but it at least gives slightly more detail.
-- >8 --
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]>
---
gcc/cp/constraint.cc | 3 +-
gcc/cp/cp-tree.h | 1 +
gcc/cp/semantics.cc | 47 +++++++++++++++----
.../is-pointer-interconvertible-base-of2.C | 29 ++++++++++++
4 files changed, 71 insertions(+), 9 deletions(-)
create mode 100644
gcc/testsuite/g++.dg/cpp2a/is-pointer-interconvertible-base-of2.C
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index f55cae37007..0fafd4ff01f 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3240,8 +3240,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 339ea062cd0..f24063372cd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8300,6 +8300,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 c818b739539..1d3ee5b6745 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -13081,26 +13081,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 00000000000..9530f9efe8a
--- /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 }
--
2.51.0