[PATCH 1/2 v2] Introduce __builtin_is_virtual_base_of
Patrick Palka
ppalka@redhat.com
Wed Sep 25 12:58:24 GMT 2024
On Wed, 31 Jul 2024, Giuseppe D'Angelo wrote:
> On 29/07/2024 22:53, Giuseppe D'Angelo wrote:
> > Hi,
> >
> > The attached patch is a stab at adding the necessary compiler builtin to
> > support std::is_virtual_base_of (P2985R0, approved for C++26). The name
> > of the builtin matches the one just merged into clang:
> >
> > https://github.com/llvm/llvm-project/issues/98310
> >
> > The next patch will add the libstdc++ bits.
>
> Hello,
>
> This is a new revision of the same patch, this time with a DCO.
>
> Thank you,
> --
> Giuseppe D'Angelo
>
> From d4340076cffc9895336e2d7afe5c416acb9aac3f Mon Sep 17 00:00:00 2001
> From: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
> Date: Mon, 29 Jul 2024 17:43:20 +0200
> Subject: [PATCH 1/2] Introduce __builtin_is_virtual_base_of
>
> 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 built-in.
> * cp-tree.h (DERIVED_VIRTUALLY_FROM_P): New macro to search
> for virtual inheritance.
> (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 using IS_VIRTUAL_BASE_OF. Unlike IS_BASE_OF, the same
> non-union class type is not a virtual base of itself.
> (finish_trait_expr): Handle the new builtin.
>
> gcc/ChangeLog:
>
> * doc/extend.texi: Document the new
> __builtin_is_virtual_base_of builtin.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/ext/is_virtual_base_of.C: New test.
> * g++.dg/ext/is_virtual_base_of_diagnostic.C: New test.
LGTM! (though I can't formally approve the patch)
>
> Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
> ---
> gcc/cp/constraint.cc | 3 +
> gcc/cp/cp-trait.def | 1 +
> gcc/cp/cp-tree.h | 8 +-
> gcc/cp/cxx-pretty-print.cc | 2 +
> gcc/cp/search.cc | 17 +-
> gcc/cp/semantics.cc | 11 ++
> gcc/doc/extend.texi | 10 ++
> gcc/testsuite/g++.dg/ext/is_virtual_base_of.C | 163 ++++++++++++++++++
> .../ext/is_virtual_base_of_diagnostic.C | 15 ++
> 9 files changed, 225 insertions(+), 5 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/cp/constraint.cc b/gcc/cp/constraint.cc
> index 853ea8c4b93..6d3a4a167aa 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3600,6 +3600,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/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)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 238d786b067..6720be3ad7e 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -2330,6 +2330,11 @@ enum languages { lang_c, lang_cplusplus };
> #define DERIVED_FROM_P(PARENT, TYPE) \
> (lookup_base ((TYPE), (PARENT), ba_any, NULL, tf_none) != NULL_TREE)
>
> +/* Nonzero iff TYPE is derived virtually from PARENT. Ignores accessibility and
> + ambiguity issues. */
> +#define DERIVED_VIRTUALLY_FROM_P(PARENT, TYPE) \
> + (lookup_base ((TYPE), (PARENT), ba_require_virtual, NULL, tf_none) != NULL_TREE)
> +
> /* Gives the visibility specification for a class type. */
> #define CLASSTYPE_VISIBILITY(TYPE) \
> DECL_VISIBILITY (TYPE_MAIN_DECL (TYPE))
> @@ -5727,7 +5732,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/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 827f48e8604..c391761482f 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;
> @@ -265,8 +273,9 @@ lookup_base (tree t, tree base, base_access access,
> data.binfo = NULL_TREE;
> data.ambiguous = data.via_virtual = false;
> data.repeated_base = (offset == -1) && CLASSTYPE_REPEATED_BASE_P (t);
> - data.want_any = access == ba_any;
> + data.want_any = (access & ~ba_require_virtual) == 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 a9abf32e01f..e3a2168e297 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12815,6 +12815,10 @@ 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)
> + && DERIVED_VIRTUALLY_FROM_P (type1, type2));
> +
> case CPTK_IS_VOLATILE:
> return CP_TYPE_VOLATILE_P (type1);
>
> @@ -13037,6 +13041,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/doc/extend.texi b/gcc/doc/extend.texi
> index 927aa24ab63..4d3a99b51bf 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -29794,6 +29794,16 @@ 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]) 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.
> +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/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);
> +}
> --
> 2.34.1
>
More information about the Libstdc++
mailing list