Skip to content

Commit

Permalink
[clang] adds __is_scoped_enum, __is_nullptr, and `__is_referencea…
Browse files Browse the repository at this point in the history
…ble`

... as builtins.

This is information that the compiler already has, and should be exposed
so that the library doesn't need to reimplement the exact same
functionality.

This was originally a part of D116280.

Depends on D135175.

Differential Revision: https://reviews.llvm.org/D135177
  • Loading branch information
cjdb committed Oct 11, 2022
1 parent bd3f48e commit a089def
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 2 deletions.
9 changes: 9 additions & 0 deletions clang/docs/LanguageExtensions.rst
Expand Up @@ -1428,16 +1428,25 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_nothrow_constructible`` (C++, MSVC 2013)
* ``__is_nothrow_destructible`` (C++, MSVC 2013)
Only available in ``-fms-extensions`` mode.
* ``__is_nullptr`` (C++, GNU, Microsoft, Embarcadero):
Returns true for ``std::nullptr_t`` and false for everything else. The
corresponding standard library feature is ``std::is_null_pointer``, but
``__is_null_pointer`` is already in use by some implementations.
* ``__is_object`` (C++, Embarcadero)
* ``__is_pod`` (C++, GNU, Microsoft, Embarcadero):
Note, the corresponding standard trait was deprecated in C++20.
* ``__is_pointer`` (C++, Embarcadero)
* ``__is_polymorphic`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_reference`` (C++, Embarcadero)
* ``__is_referenceable`` (C++, GNU, Microsoft, Embarcadero):
Returns true if a type is referenceable, and false otherwise. A referenceable
type is a type that's either an object type, a reference type, or an unqualified
function type.
* ``__is_rvalue_reference`` (C++, Embarcadero)
* ``__is_same`` (C++, Embarcadero)
* ``__is_same_as`` (GCC): Synonym for ``__is_same``.
* ``__is_scalar`` (C++, Embarcadero)
* ``__is_scoped_enum`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_sealed`` (Microsoft):
Synonym for ``__is_final``.
* ``__is_signed`` (C++, Embarcadero):
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Expand Up @@ -520,6 +520,9 @@ TYPE_TRAIT_1(__has_unique_object_representations,
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_1(__is_bounded_array, IsBoundedArray, KEYCXX)
TYPE_TRAIT_1(__is_unbounded_array, IsUnboundedArray, KEYCXX)
TYPE_TRAIT_1(__is_nullptr, IsNullPointer, KEYCXX)
TYPE_TRAIT_1(__is_scoped_enum, IsScopedEnum, KEYCXX)
TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX)
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)

// Embarcadero Expression Traits
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Expand Up @@ -1605,15 +1605,18 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
tok::kw___is_nothrow_assignable,
tok::kw___is_nothrow_constructible,
tok::kw___is_nothrow_destructible,
tok::kw___is_nullptr,
tok::kw___is_object,
tok::kw___is_pod,
tok::kw___is_pointer,
tok::kw___is_polymorphic,
tok::kw___is_reference,
tok::kw___is_referenceable,
tok::kw___is_rvalue_expr,
tok::kw___is_rvalue_reference,
tok::kw___is_same,
tok::kw___is_scalar,
tok::kw___is_scoped_enum,
tok::kw___is_sealed,
tok::kw___is_signed,
tok::kw___is_standard_layout,
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Parse/ParseExpr.cpp
Expand Up @@ -1067,8 +1067,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
REVERTIBLE_TYPE_TRAIT(__is_arithmetic);
REVERTIBLE_TYPE_TRAIT(__is_array);
REVERTIBLE_TYPE_TRAIT(__is_assignable);
REVERTIBLE_TYPE_TRAIT(__is_bounded_array);
REVERTIBLE_TYPE_TRAIT(__is_base_of);
REVERTIBLE_TYPE_TRAIT(__is_bounded_array);
REVERTIBLE_TYPE_TRAIT(__is_class);
REVERTIBLE_TYPE_TRAIT(__is_complete_type);
REVERTIBLE_TYPE_TRAIT(__is_compound);
Expand All @@ -1094,15 +1094,18 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
REVERTIBLE_TYPE_TRAIT(__is_nothrow_assignable);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_constructible);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_destructible);
REVERTIBLE_TYPE_TRAIT(__is_nullptr);
REVERTIBLE_TYPE_TRAIT(__is_object);
REVERTIBLE_TYPE_TRAIT(__is_pod);
REVERTIBLE_TYPE_TRAIT(__is_pointer);
REVERTIBLE_TYPE_TRAIT(__is_polymorphic);
REVERTIBLE_TYPE_TRAIT(__is_reference);
REVERTIBLE_TYPE_TRAIT(__is_referenceable);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_reference);
REVERTIBLE_TYPE_TRAIT(__is_same);
REVERTIBLE_TYPE_TRAIT(__is_scalar);
REVERTIBLE_TYPE_TRAIT(__is_scoped_enum);
REVERTIBLE_TYPE_TRAIT(__is_sealed);
REVERTIBLE_TYPE_TRAIT(__is_signed);
REVERTIBLE_TYPE_TRAIT(__is_standard_layout);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -4760,11 +4760,14 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsArray:
case UTT_IsBoundedArray:
case UTT_IsPointer:
case UTT_IsNullPointer:
case UTT_IsReferenceable:
case UTT_IsLvalueReference:
case UTT_IsRvalueReference:
case UTT_IsMemberFunctionPointer:
case UTT_IsMemberObjectPointer:
case UTT_IsEnum:
case UTT_IsScopedEnum:
case UTT_IsUnion:
case UTT_IsClass:
case UTT_IsFunction:
Expand Down Expand Up @@ -4923,6 +4926,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return false;
case UTT_IsPointer:
return T->isAnyPointerType();
case UTT_IsNullPointer:
return T->isNullPtrType();
case UTT_IsLvalueReference:
return T->isLValueReferenceType();
case UTT_IsRvalueReference:
Expand All @@ -4933,6 +4938,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return T->isMemberDataPointerType();
case UTT_IsEnum:
return T->isEnumeralType();
case UTT_IsScopedEnum:
return T->isScopedEnumeralType();
case UTT_IsUnion:
return T->isUnionType();
case UTT_IsClass:
Expand Down Expand Up @@ -5304,6 +5311,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return C.hasUniqueObjectRepresentations(T);
case UTT_IsTriviallyRelocatable:
return T.isTriviallyRelocatableType(C);
case UTT_IsReferenceable:
return T.isReferenceable();
}
}

Expand Down
108 changes: 107 additions & 1 deletion clang/test/SemaCXX/type-traits.cpp
Expand Up @@ -345,11 +345,19 @@ void is_union()
}

typedef Enum EnumType;
typedef EnumClass EnumClassType;

void is_enum()
{
{ int arr[T(__is_enum(Enum))]; }
{ int arr[T(__is_enum(EnumType))]; }
{ int arr[T(__is_enum(SignedEnum))]; }
{ int arr[T(__is_enum(UnsignedEnum))]; }

{ int arr[T(__is_enum(EnumClass))]; }
{ int arr[T(__is_enum(EnumClassType))]; }
{ int arr[T(__is_enum(SignedEnumClass))]; }
{ int arr[T(__is_enum(UnsignedEnumClass))]; }

{ int arr[F(__is_enum(int))]; }
{ int arr[F(__is_enum(Union))]; }
Expand All @@ -361,6 +369,37 @@ void is_enum()
{ int arr[F(__is_enum(cvoid))]; }
{ int arr[F(__is_enum(IntArNB))]; }
{ int arr[F(__is_enum(HasAnonymousUnion))]; }
{ int arr[F(__is_enum(AnIncompleteType))]; }
{ int arr[F(__is_enum(AnIncompleteTypeAr))]; }
{ int arr[F(__is_enum(AnIncompleteTypeArMB))]; }
{ int arr[F(__is_enum(AnIncompleteTypeArNB))]; }
}

void is_scoped_enum() {
static_assert(!__is_scoped_enum(Enum), "");
static_assert(!__is_scoped_enum(EnumType), "");
static_assert(!__is_scoped_enum(SignedEnum), "");
static_assert(!__is_scoped_enum(UnsignedEnum), "");

static_assert(__is_scoped_enum(EnumClass), "");
static_assert(__is_scoped_enum(EnumClassType), "");
static_assert(__is_scoped_enum(SignedEnumClass), "");
static_assert(__is_scoped_enum(UnsignedEnumClass), "");

static_assert(!__is_scoped_enum(int), "");
static_assert(!__is_scoped_enum(Union), "");
static_assert(!__is_scoped_enum(Int), "");
static_assert(!__is_scoped_enum(IntAr), "");
static_assert(!__is_scoped_enum(UnionAr), "");
static_assert(!__is_scoped_enum(Derives), "");
static_assert(!__is_scoped_enum(ClassType), "");
static_assert(!__is_scoped_enum(cvoid), "");
static_assert(!__is_scoped_enum(IntArNB), "");
static_assert(!__is_scoped_enum(HasAnonymousUnion), "");
static_assert(!__is_scoped_enum(AnIncompleteType), "");
static_assert(!__is_scoped_enum(AnIncompleteTypeAr), "");
static_assert(!__is_scoped_enum(AnIncompleteTypeArMB), "");
static_assert(!__is_scoped_enum(AnIncompleteTypeArNB), "");
}

struct FinalClass final {
Expand Down Expand Up @@ -766,6 +805,36 @@ void is_unbounded_array(int n) {
(void)__is_unbounded_array(decltype(t32)); // expected-error{{variable length arrays are not supported for '__is_unbounded_array'}}
}

void is_referenceable() {
static_assert(__is_referenceable(int), "");
static_assert(__is_referenceable(const int), "");
static_assert(__is_referenceable(volatile int), "");
static_assert(__is_referenceable(const volatile int), "");
static_assert(__is_referenceable(int *), "");
static_assert(__is_referenceable(int &), "");
static_assert(__is_referenceable(int &&), "");
static_assert(__is_referenceable(int (*)()), "");
static_assert(__is_referenceable(int (&)()), "");
static_assert(__is_referenceable(int(&&)()), "");
static_assert(__is_referenceable(IntAr), "");
static_assert(__is_referenceable(IntArNB), "");
static_assert(__is_referenceable(decltype(nullptr)), "");
static_assert(__is_referenceable(Empty), "");
static_assert(__is_referenceable(Union), "");
static_assert(__is_referenceable(Derives), "");
static_assert(__is_referenceable(Enum), "");
static_assert(__is_referenceable(EnumClass), "");
static_assert(__is_referenceable(int Empty::*), "");
static_assert(__is_referenceable(int(Empty::*)()), "");
static_assert(__is_referenceable(AnIncompleteType), "");
static_assert(__is_referenceable(struct AnIncompleteType), "");

using function_type = void(int);
static_assert(__is_referenceable(function_type), "");

static_assert(!__is_referenceable(void), "");
}

template <typename T> void tmpl_func(T&) {}

template <typename T> struct type_wrapper {
Expand Down Expand Up @@ -998,6 +1067,42 @@ void is_pointer()
int t34[F(__is_pointer(void (StructWithMembers::*) ()))];
}

void is_null_pointer() {
StructWithMembers x;

static_assert(__is_nullptr(decltype(nullptr)), "");
static_assert(!__is_nullptr(void *), "");
static_assert(!__is_nullptr(cvoid *), "");
static_assert(!__is_nullptr(cvoid *), "");
static_assert(!__is_nullptr(char *), "");
static_assert(!__is_nullptr(int *), "");
static_assert(!__is_nullptr(int **), "");
static_assert(!__is_nullptr(ClassType *), "");
static_assert(!__is_nullptr(Derives *), "");
static_assert(!__is_nullptr(Enum *), "");
static_assert(!__is_nullptr(IntArNB *), "");
static_assert(!__is_nullptr(Union *), "");
static_assert(!__is_nullptr(UnionAr *), "");
static_assert(!__is_nullptr(StructWithMembers *), "");
static_assert(!__is_nullptr(void (*)()), "");

static_assert(!__is_nullptr(void), "");
static_assert(!__is_nullptr(cvoid), "");
static_assert(!__is_nullptr(cvoid), "");
static_assert(!__is_nullptr(char), "");
static_assert(!__is_nullptr(int), "");
static_assert(!__is_nullptr(int), "");
static_assert(!__is_nullptr(ClassType), "");
static_assert(!__is_nullptr(Derives), "");
static_assert(!__is_nullptr(Enum), "");
static_assert(!__is_nullptr(IntArNB), "");
static_assert(!__is_nullptr(Union), "");
static_assert(!__is_nullptr(UnionAr), "");
static_assert(!__is_nullptr(StructWithMembers), "");
static_assert(!__is_nullptr(int StructWithMembers::*), "");
static_assert(!__is_nullptr(void(StructWithMembers::*)()), "");
}

void is_member_object_pointer()
{
StructWithMembers x;
Expand Down Expand Up @@ -3299,7 +3404,6 @@ void check_remove_cvref() {
}

template <class T> using decay_t = __decay(T);
template <class T> struct dne;

void check_decay() {
static_assert(__is_same(decay_t<void>, void), "");
Expand Down Expand Up @@ -3359,6 +3463,8 @@ struct CheckAbominableFunction<M S::*> {
static_assert(__is_same(remove_cvref_t<M>, M), "");
static_assert(__is_same(remove_pointer_t<M>, M), "");
static_assert(__is_same(remove_reference_t<M>, M), "");

static_assert(!__is_referenceable(M), "");
}
};

Expand Down

0 comments on commit a089def

Please sign in to comment.