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

Reply via email to