c++: 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 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.dangelo@kdab.com> Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
parent
698e0ec89b
commit
1b7cfa715c
9 changed files with 225 additions and 4 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}.
|
||||
|
|
163
gcc/testsuite/g++.dg/ext/is_virtual_base_of.C
Normal file
163
gcc/testsuite/g++.dg/ext/is_virtual_base_of.C
Normal file
|
@ -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;
|
||||
}
|
15
gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C
Normal file
15
gcc/testsuite/g++.dg/ext/is_virtual_base_of_diagnostic.C
Normal file
|
@ -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);
|
||||
}
|
Loading…
Add table
Reference in a new issue