On 10/1/24 7:31 AM, Giuseppe D'Angelo wrote:
Hello,

Attaching an updated patch.

Thanks, I'm pushing this with a few tweaks:

+unaccessible base class of @var{derived_type}.

"inaccessible"

+      const bool via_virtual =
+           binfo_via_virtual (binfo, data->t) != NULL_TREE;

Moved = to the next line.

+
+      if (data->require_virtual && !via_virtual)
+           /* Skip this result if we require virtual inheritance
+              and this is not a virtual base.  */
+           return dfs_skip_bases;

Fixed indentation.

+      return NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
+             && lookup_base (type2, type1, ba_require_virtual, NULL, tf_none) 
!= NULL_TREE;

Added parens and wrapped too-long line.

Jason
From 2aec78ba1c8f74b2f9417291323d308c71135ea9 Mon Sep 17 00:00:00 2001
From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date: Tue, 1 Oct 2024 13:31:00 +0200
Subject: [PATCH] c++: introduce __builtin_is_virtual_base_of
To: gcc-patches@gcc.gnu.org

P2985R0 (C++26) introduces std::is_virtual_base_of; this is the compiler
builtin that will back up the library trait (which strictly requires
compiler support).

The name has been chosen to match LLVM/MSVC's, as per the discussion
here:

https://github.com/llvm/llvm-project/issues/98310

The actual user-facing type trait in libstdc++ will be added later.

gcc/cp/ChangeLog:

	* constraint.cc (diagnose_trait_expr): New diagnostic.
	* cp-trait.def (IS_VIRTUAL_BASE_OF): New builtin.
	* cp-tree.h (enum base_access_flags): Add a new flag to be
	able to request a search for a virtual base class.
	* cxx-pretty-print.cc (pp_cxx_userdef_literal): Update the
	list of GNU extensions to the grammar.
	* search.cc (struct lookup_base_data_s): Add a field to
	request searching for a virtual base class.
	(dfs_lookup_base): Add the ability to look for a virtual
	base class.
	(lookup_base): Forward the flag to dfs_lookup_base.
	* semantics.cc (trait_expr_value): Implement the builtin
	by calling lookup_base with the new flag.
	(finish_trait_expr): Handle the new builtin.

gcc/ChangeLog:

	* doc/extend.texi: Document the new
	__builtin_is_virtual_base_of builtin; amend the docs for
	__is_base_of.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/is_virtual_base_of.C: New test.
	* g++.dg/ext/is_virtual_base_of_diagnostic.C: New test.

Signed-off-by: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Reviewed-by: Jason Merrill <ja...@redhat.com>
---
 gcc/doc/extend.texi                           |  15 ++
 gcc/cp/cp-tree.h                              |   3 +-
 gcc/cp/constraint.cc                          |   3 +
 gcc/cp/cxx-pretty-print.cc                    |   2 +
 gcc/cp/search.cc                              |  15 +-
 gcc/cp/semantics.cc                           |  12 ++
 gcc/testsuite/g++.dg/ext/is_virtual_base_of.C | 163 ++++++++++++++++++
 .../ext/is_virtual_base_of_diagnostic.C       |  15 ++
 gcc/cp/cp-trait.def                           |   1 +
 9 files changed, 225 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_virtual_base_of.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f0d8eb59dde..f46c3df3303 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -29984,12 +29984,27 @@ If @var{base_type} is a base class of @var{derived_type}
 Top-level cv-qualifications of @var{base_type} and
 @var{derived_type} are ignored.  For the purposes of this trait, a
 class type is considered is own base.
+The trait is @code{true} even if @var{base_type} is an ambiguous or
+inaccessible base class of @var{derived_type}.
 Requires: if @code{__is_class (base_type)} and @code{__is_class (derived_type)}
 are @code{true} and @var{base_type} and @var{derived_type} are not the same
 type (disregarding cv-qualifiers), @var{derived_type} shall be a complete
 type.  A diagnostic is produced if this requirement is not met.
 @enddefbuiltin
 
+@defbuiltin{bool __builtin_is_virtual_base_of (@var{base_type}, @var{derived_type})}
+If @var{base_type} is a virtual base class of @var{derived_type}
+([class.derived], [class.mi]) then the trait is @code{true},
+otherwise it is @code{false}.
+Top-level cv-qualifications of @var{base_type} and
+@var{derived_type} are ignored.
+The trait is @code{true} even if @var{base_type} is an ambiguous or
+inaccessible virtual base class of @var{derived_type}.
+Requires: if @code{__is_class (base_type)} and @code{__is_class (derived_type)}
+are @code{true}, @var{derived_type} shall be a complete
+type.  A diagnostic is produced if this requirement is not met.
+@enddefbuiltin
+
 @defbuiltin{bool __is_class (@var{type})}
 If @var{type} is a cv-qualified class type, and not a union type
 ([basic.compound]) the trait is @code{true}, else it is @code{false}.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 39c065eecf6..c5d02567cb4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5723,7 +5723,8 @@ enum base_access_flags {
   ba_unique = 1 << 0,  /* Must be a unique base.  */
   ba_check_bit = 1 << 1,   /* Check access.  */
   ba_check = ba_unique | ba_check_bit,
-  ba_ignore_scope = 1 << 2 /* Ignore access allowed by local scope.  */
+  ba_ignore_scope = 1 << 2, /* Ignore access allowed by local scope.  */
+  ba_require_virtual = 1 << 3 /* Require a virtual base.  */
 };
 
 /* This type is used for parameters and variables which hold
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index ebfcdefd284..cf0e5d37571 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3291,6 +3291,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VIRTUAL_BASE_OF:
+      inform (loc, "  %qT is not a virtual base of %qT", t1, t2);
+      break;
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index e690354e08e..41e6bdfdda5 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -418,6 +418,8 @@ pp_cxx_userdef_literal (cxx_pretty_printer *pp, tree t)
      __builtin_offsetof ( type-id, offsetof-expression )
      __builtin_addressof ( expression )
 
+     __builtin_is_virtual_base_of ( type-id , type-id )
+
      __has_nothrow_assign ( type-id )   
      __has_nothrow_constructor ( type-id )
      __has_nothrow_copy ( type-id )
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 6a21a25272b..b0421ee0524 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -65,6 +65,7 @@ struct lookup_base_data_s
   bool repeated_base;	/* Whether there are repeated bases in the
 			    hierarchy.  */
   bool want_any;	/* Whether we want any matching binfo.  */
+  bool require_virtual;	/* Whether we require a virtual path.  */
 };
 
 /* Worker function for lookup_base.  See if we've found the desired
@@ -93,11 +94,18 @@ dfs_lookup_base (tree binfo, void *data_)
 
   if (SAME_BINFO_TYPE_P (BINFO_TYPE (binfo), data->base))
     {
+      const bool via_virtual
+	= binfo_via_virtual (binfo, data->t) != NULL_TREE;
+
+      if (data->require_virtual && !via_virtual)
+	/* Skip this result if we require virtual inheritance
+	   and this is not a virtual base.  */
+	return dfs_skip_bases;
+
       if (!data->binfo)
 	{
 	  data->binfo = binfo;
-	  data->via_virtual
-	    = binfo_via_virtual (data->binfo, data->t) != NULL_TREE;
+	  data->via_virtual = via_virtual;
 
 	  if (!data->repeated_base)
 	    /* If there are no repeated bases, we can stop now.  */
@@ -124,7 +132,7 @@ dfs_lookup_base (tree binfo, void *data_)
 	    }
 
 	  /* Prefer one via a non-virtual path.  */
-	  if (!binfo_via_virtual (binfo, data->t))
+	  if (!via_virtual)
 	    {
 	      data->binfo = binfo;
 	      data->via_virtual = false;
@@ -271,6 +279,7 @@ lookup_base (tree t, tree base, base_access access,
       data.repeated_base = (offset == -1) && CLASSTYPE_REPEATED_BASE_P (t);
       data.want_any = access == ba_any;
       data.offset = offset;
+      data.require_virtual = (access & ba_require_virtual);
 
       dfs_walk_once (t_binfo, dfs_lookup_base, NULL, &data);
       binfo = data.binfo;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0cb46c1986c..0370d81de01 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12932,6 +12932,11 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VIRTUAL_BASE_OF:
+      return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
+	      && lookup_base (type2, type1, ba_require_virtual,
+			      NULL, tf_none) != NULL_TREE);
+
     case CPTK_IS_VOLATILE:
       return CP_TYPE_VOLATILE_P (type1);
 
@@ -13154,6 +13159,13 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 	return error_mark_node;
       break;
 
+    case CPTK_IS_VIRTUAL_BASE_OF:
+      if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
+	  && !complete_type_or_else (type2, NULL_TREE))
+	/* We already issued an error.  */
+	return error_mark_node;
+      break;
+
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
diff --git a/gcc/testsuite/g++.dg/ext/is_virtual_base_of.C b/gcc/testsuite/g++.dg/ext/is_virtual_base_of.C
new file mode 100644
index 00000000000..775a3539388
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_virtual_base_of.C
@@ -0,0 +1,163 @@
+// { dg-do run }
+#include <cassert>
+
+class A1
+{ 
+  double a;
+  double b;  
+};
+
+class A2
+{ 
+  double a;
+  double b;
+};
+
+class B
+: private A1 { };
+
+class C
+: private A1, private A2 { };
+
+class VD1
+: public virtual A1 { };
+
+class VD2
+: protected virtual A1 { };
+
+class VD3
+: private virtual A1 { };
+
+class D
+: public B, public VD1 { };
+
+class VDMultiple1
+: public virtual A1, public A2 { };
+
+class VDMultiple2
+: public virtual A1, public virtual A2 { };
+
+template <typename T>
+class VDTemplate : public virtual T { };
+
+// example from from [class.mi]
+namespace class_mi
+{
+class B { int b; };
+class X : virtual public B { int x; };
+class Y : virtual public B { int y; };
+class Z : public B { int z; };
+class AA : public X, public Y, public Z { int aa; };
+}
+
+union U
+{ 
+  double a;
+  double b;
+};
+
+template<typename T, typename U>
+  bool
+  f()
+  { return __builtin_is_virtual_base_of(T, U); } 
+
+template<typename T, typename U>
+  class My
+  {
+  public:
+    bool
+    f()
+    { return !!__builtin_is_virtual_base_of(T, U); }
+  };
+
+template<typename T, typename U>
+  class My2
+  {
+  public:
+    static const bool trait = __builtin_is_virtual_base_of(T, U);
+  };
+
+template<typename T, typename U>
+  const bool My2<T, U>::trait;
+
+template<typename T, typename U, bool b = __builtin_is_virtual_base_of(T, U)>
+  struct My3_help
+  { static const bool trait = b; };
+
+template<typename T, typename U, bool b>
+  const bool My3_help<T, U, b>::trait;
+
+template<typename T, typename U>
+  class My3
+  {
+  public:
+    bool
+    f()
+    { return My3_help<T, U>::trait; }
+  };
+
+#define PTEST(T, U) (__builtin_is_virtual_base_of(T, U) && f<T, U>() \
+                  && My<T, U>().f() && My2<T, U>::trait && My3<T, U>().f())
+
+#define NTEST(T, U) (!__builtin_is_virtual_base_of(T, U) && !f<T, U>() \
+                  && !My<T, U>().f() && !My2<T, U>::trait && !My3<T, U>().f())
+
+int main()
+{
+  assert (NTEST (int, A1));
+  assert (NTEST (A1, void));
+  assert (NTEST (A1, A1));
+  assert (NTEST (A1*, A1*));
+  assert (NTEST (A1&, A1&));
+  assert (NTEST (A1, B));
+  assert (NTEST (B, A1));
+  assert (NTEST (A1, C));
+  assert (NTEST (A2, C));
+  assert (NTEST (C, A1));
+  assert (NTEST (A1, const B));
+  assert (NTEST (const B, A1));
+  assert (NTEST (A1, volatile C));
+  assert (NTEST (volatile A2, const C));
+  assert (NTEST (const volatile C, A1));
+  
+  assert (PTEST (A1, VD1));
+  assert (PTEST (A1, VD2));
+  assert (PTEST (A1, VD3));
+  
+  assert (PTEST (A1, const VD1));
+  assert (PTEST (A1, const VD2));
+  assert (PTEST (A1, const VD3));
+  
+  assert (PTEST (const A1, VD1));
+  assert (PTEST (const A1, VD2));
+  assert (PTEST (const A1, VD3));
+  
+  assert (PTEST (const A1, const VD1));
+  assert (PTEST (const A1, const VD2));
+  assert (PTEST (const A1, const VD3));
+  
+  assert (NTEST (A2, VD1));
+  assert (NTEST (A2, VD2));
+  assert (NTEST (A2, VD3));
+  
+  assert (PTEST (A1, D));
+  
+  assert (PTEST (A1, VDMultiple1));
+  assert (PTEST (A1, VDMultiple2));
+  assert (NTEST (A2, VDMultiple1));
+  assert (PTEST (A2, VDMultiple2));  
+  
+  assert (PTEST (A1, VDTemplate<A1>));
+  assert (NTEST (A2, VDTemplate<A1>));
+  assert (NTEST (A1, VDTemplate<A2>));
+
+  assert (NTEST (class_mi::B, class_mi::B));
+  assert (PTEST (class_mi::B, class_mi::X));
+  assert (PTEST (class_mi::B, class_mi::Y));
+  assert (NTEST (class_mi::B, class_mi::Z));
+  assert (PTEST (class_mi::B, class_mi::AA));
+
+  assert (NTEST (U, U));
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C b/gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C
new file mode 100644
index 00000000000..a50459afdca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C
@@ -0,0 +1,15 @@
+class A
+{ };
+
+class B; // { dg-message "forward declaration" }
+
+union C
+{ };
+
+union D;
+
+void f()
+{
+  __builtin_is_virtual_base_of(A, B);  // { dg-error "incomplete type" }
+  __builtin_is_virtual_base_of(C, D);  
+}
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 0721ecdf3f0..6aaca13c5e9 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VIRTUAL_BASE_OF, "__builtin_is_virtual_base_of", 2)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (RANK, "__array_rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
-- 
2.46.2

Reply via email to