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