Skip to content

Commit

Permalink
[clang] Implement __builtin_is_implicit_lifetime() (#101807)
Browse files Browse the repository at this point in the history
This intrinsic supports [P2647R1](https://wg21.link/p2674r1) "A trait
for implicit lifetime types".
Resolves #98627

---------

Co-authored-by: Timm Baeder <[email protected]>
  • Loading branch information
Endilll and tbaederr authored Aug 14, 2024
1 parent d0c16a6 commit d213981
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 1 deletion.
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__array_extent(type, dim)`` (Embarcadero):
The ``dim``'th array bound in the type ``type``, or ``0`` if
``dim >= __array_rank(type)``.
* ``__builtin_is_implicit_lifetime`` (C++, GNU, Microsoft)
* ``__builtin_is_virtual_base_of`` (C++, GNU, Microsoft)
* ``__can_pass_in_regs`` (C++)
Returns whether a class can be passed in registers under the current
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ C++23 Feature Support
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

- Add ``__builtin_is_implicit_lifetime`` intrinsic, which supports
`P2647R1 A trait for implicit lifetime types <https://wg21.link/p2674r1>`_

- Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
`P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -8968,6 +8968,8 @@ def err_atomic_op_has_invalid_synch_scope : Error<
def warn_atomic_implicit_seq_cst : Warning<
"implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary">,
InGroup<DiagGroup<"atomic-implicit-seq-cst">>, DefaultIgnore;
def err_atomic_unsupported : Error<
"atomic types are not supported in '%0'">;

def err_overflow_builtin_must_be_int : Error<
"operand argument to %select{overflow builtin|checked integer operation}0 "
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX)
TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX)

// GNU and MS Type Traits
TYPE_TRAIT_1(__builtin_is_implicit_lifetime, IsImplicitLifetime, KEYCXX)
TYPE_TRAIT_2(__builtin_is_virtual_base_of, IsVirtualBaseOf, KEYCXX)
TYPE_TRAIT_1(__has_nothrow_assign, HasNothrowAssign, KEYCXX)
TYPE_TRAIT_1(__has_nothrow_copy, HasNothrowCopy, KEYCXX)
Expand Down
49 changes: 49 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4948,6 +4948,20 @@ static bool DiagnoseVLAInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
return true;
}

/// Checks that type T is not an atomic type (_Atomic).
///
/// @returns @c true if @p T is VLA and a diagnostic was emitted,
/// @c false otherwise.
static bool DiagnoseAtomicInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
clang::tok::TokenKind TypeTraitID) {
if (!T->getType()->isAtomicType())
return false;

S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_atomic_unsupported)
<< TypeTraitID;
return true;
}

/// Check the completeness of a type in a unary type trait.
///
/// If the particular type trait requires a complete type, tries to complete
Expand Down Expand Up @@ -5038,6 +5052,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,

// LWG3823: T shall be an array type, a complete type, or cv void.
case UTT_IsAggregate:
case UTT_IsImplicitLifetime:
if (ArgTy->isArrayType() || ArgTy->isVoidType())
return true;

Expand Down Expand Up @@ -5634,6 +5649,40 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return false;
case UTT_IsTriviallyEqualityComparable:
return isTriviallyEqualityComparableType(Self, T, KeyLoc);
case UTT_IsImplicitLifetime: {
DiagnoseVLAInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_is_implicit_lifetime);
DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_is_implicit_lifetime);

// [basic.types.general] p9
// Scalar types, implicit-lifetime class types ([class.prop]),
// array types, and cv-qualified versions of these types
// are collectively called implicit-lifetime types.
QualType UnqualT = T->getCanonicalTypeUnqualified();
if (UnqualT->isScalarType())
return true;
if (UnqualT->isArrayType() || UnqualT->isVectorType())
return true;
const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
if (!RD)
return false;

// [class.prop] p9
// A class S is an implicit-lifetime class if
// - it is an aggregate whose destructor is not user-provided or
// - it has at least one trivial eligible constructor and a trivial,
// non-deleted destructor.
const CXXDestructorDecl *Dtor = RD->getDestructor();
if (UnqualT->isAggregateType())
if (Dtor && !Dtor->isUserProvided())
return true;
if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
if (RD->hasTrivialDefaultConstructor() ||
RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
return true;
return false;
}
}
}

Expand Down
158 changes: 157 additions & 1 deletion clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ enum class SignedEnumClass : signed int {};
enum class UnsignedEnumClass : unsigned int {};
struct POD { Enum e; int i; float f; NonPOD* p; };
struct Empty {};
struct IncompleteStruct;
struct IncompleteStruct; // expected-note {{forward declaration of 'IncompleteStruct'}}
typedef Empty EmptyAr[10];
typedef Empty EmptyArNB[];
typedef Empty EmptyArMB[1][2];
Expand Down Expand Up @@ -1908,6 +1908,162 @@ void is_pointer_interconvertible_base_of(int n)
}
}

struct NoEligibleTrivialContructor {
NoEligibleTrivialContructor() {};
NoEligibleTrivialContructor(const NoEligibleTrivialContructor&) {}
NoEligibleTrivialContructor(NoEligibleTrivialContructor&&) {}
};

struct OnlyDefaultConstructorIsTrivial {
OnlyDefaultConstructorIsTrivial() = default;
OnlyDefaultConstructorIsTrivial(const OnlyDefaultConstructorIsTrivial&) {}
OnlyDefaultConstructorIsTrivial(OnlyDefaultConstructorIsTrivial&&) {}
};

struct AllContstructorsAreTrivial {
AllContstructorsAreTrivial() = default;
AllContstructorsAreTrivial(const AllContstructorsAreTrivial&) = default;
AllContstructorsAreTrivial(AllContstructorsAreTrivial&&) = default;
};

struct InheritedNoEligibleTrivialConstructor : NoEligibleTrivialContructor {
using NoEligibleTrivialContructor::NoEligibleTrivialContructor;
};

struct InheritedOnlyDefaultConstructorIsTrivial : OnlyDefaultConstructorIsTrivial {
using OnlyDefaultConstructorIsTrivial::OnlyDefaultConstructorIsTrivial;
};

struct InheritedAllContstructorsAreTrivial : AllContstructorsAreTrivial {
using AllContstructorsAreTrivial::AllContstructorsAreTrivial;
};

struct UserDeclaredDestructor {
~UserDeclaredDestructor() = default;
};

struct UserProvidedDestructor {
~UserProvidedDestructor() {}
};

struct UserDeletedDestructorInAggregate {
~UserDeletedDestructorInAggregate() = delete;
};

struct UserDeletedDestructorInNonAggregate {
virtual void NonAggregate();
~UserDeletedDestructorInNonAggregate() = delete;
};

struct DeletedDestructorViaBaseInAggregate : UserDeletedDestructorInAggregate {};
struct DeletedDestructorViaBaseInNonAggregate : UserDeletedDestructorInNonAggregate {};

#if __cplusplus >= 202002L
template<bool B>
struct ConstrainedUserDeclaredDefaultConstructor{
ConstrainedUserDeclaredDefaultConstructor() requires B = default;
ConstrainedUserDeclaredDefaultConstructor(const ConstrainedUserDeclaredDefaultConstructor&) {}
};

template<bool B>
struct ConstrainedUserProvidedDestructor {
~ConstrainedUserProvidedDestructor() = default;
~ConstrainedUserProvidedDestructor() requires B {}
};
#endif

struct StructWithFAM {
int a[];
};

struct StructWithZeroSizedArray {
int a[0];
};

typedef float float4 __attribute__((ext_vector_type(4)));
typedef int *align_value_int __attribute__((align_value(16)));

struct [[clang::enforce_read_only_placement]] EnforceReadOnlyPlacement {};
struct [[clang::type_visibility("hidden")]] TypeVisibility {};

void is_implicit_lifetime(int n) {
static_assert(__builtin_is_implicit_lifetime(decltype(nullptr)));
static_assert(!__builtin_is_implicit_lifetime(void));
static_assert(!__builtin_is_implicit_lifetime(const void));
static_assert(!__builtin_is_implicit_lifetime(volatile void));
static_assert(__builtin_is_implicit_lifetime(int));
static_assert(!__builtin_is_implicit_lifetime(int&));
static_assert(!__builtin_is_implicit_lifetime(int&&));
static_assert(__builtin_is_implicit_lifetime(float));
static_assert(__builtin_is_implicit_lifetime(double));
static_assert(__builtin_is_implicit_lifetime(long double));
static_assert(__builtin_is_implicit_lifetime(int*));
static_assert(__builtin_is_implicit_lifetime(int[]));
static_assert(__builtin_is_implicit_lifetime(int[5]));
static_assert(__builtin_is_implicit_lifetime(int[n]));
// expected-error@-1 {{variable length arrays are not supported in '__builtin_is_implicit_lifetime'}}
static_assert(__builtin_is_implicit_lifetime(Enum));
static_assert(__builtin_is_implicit_lifetime(EnumClass));
static_assert(!__builtin_is_implicit_lifetime(void()));
static_assert(!__builtin_is_implicit_lifetime(void() &));
static_assert(!__builtin_is_implicit_lifetime(void() const));
static_assert(!__builtin_is_implicit_lifetime(void(&)()));
static_assert(__builtin_is_implicit_lifetime(void(*)()));
static_assert(__builtin_is_implicit_lifetime(decltype(nullptr)));
static_assert(__builtin_is_implicit_lifetime(int UserDeclaredDestructor::*));
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)()));
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() const));
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &));
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &&));
static_assert(!__builtin_is_implicit_lifetime(IncompleteStruct));
// expected-error@-1 {{incomplete type 'IncompleteStruct' used in type trait expression}}
static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[]));
static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[5]));
static_assert(__builtin_is_implicit_lifetime(UserDeclaredDestructor));
static_assert(__builtin_is_implicit_lifetime(const UserDeclaredDestructor));
static_assert(__builtin_is_implicit_lifetime(volatile UserDeclaredDestructor));
static_assert(!__builtin_is_implicit_lifetime(UserProvidedDestructor));
static_assert(!__builtin_is_implicit_lifetime(NoEligibleTrivialContructor));
static_assert(__builtin_is_implicit_lifetime(OnlyDefaultConstructorIsTrivial));
static_assert(__builtin_is_implicit_lifetime(AllContstructorsAreTrivial));
static_assert(!__builtin_is_implicit_lifetime(InheritedNoEligibleTrivialConstructor));
static_assert(__builtin_is_implicit_lifetime(InheritedOnlyDefaultConstructorIsTrivial));
static_assert(__builtin_is_implicit_lifetime(InheritedAllContstructorsAreTrivial));
static_assert(__builtin_is_implicit_lifetime(UserDeletedDestructorInAggregate));
static_assert(!__builtin_is_implicit_lifetime(UserDeletedDestructorInNonAggregate));
static_assert(__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInAggregate) == __cplusplus >= 201703L);
static_assert(!__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInNonAggregate));
#if __cplusplus >= 202002L
static_assert(__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<true>));
static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<false>));
static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<true>));
static_assert(__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<false>));
#endif

static_assert(__builtin_is_implicit_lifetime(__int128));
static_assert(__builtin_is_implicit_lifetime(_BitInt(8)));
static_assert(__builtin_is_implicit_lifetime(_BitInt(128)));
static_assert(__builtin_is_implicit_lifetime(int[0]));
static_assert(__builtin_is_implicit_lifetime(StructWithFAM));
static_assert(__builtin_is_implicit_lifetime(StructWithZeroSizedArray));
static_assert(__builtin_is_implicit_lifetime(__fp16));
static_assert(__builtin_is_implicit_lifetime(__bf16));
static_assert(__builtin_is_implicit_lifetime(_Complex double));
static_assert(__builtin_is_implicit_lifetime(float4));
static_assert(__builtin_is_implicit_lifetime(align_value_int));
static_assert(__builtin_is_implicit_lifetime(int[[clang::annotate_type("category2")]] *));
static_assert(__builtin_is_implicit_lifetime(int __attribute__((btf_type_tag("user"))) *));
static_assert(__builtin_is_implicit_lifetime(EnforceReadOnlyPlacement));
static_assert(__builtin_is_implicit_lifetime(int __attribute__((noderef)) *));
static_assert(__builtin_is_implicit_lifetime(TypeVisibility));
static_assert(__builtin_is_implicit_lifetime(int * _Nonnull));
static_assert(__builtin_is_implicit_lifetime(int * _Null_unspecified));
static_assert(__builtin_is_implicit_lifetime(int * _Nullable));
static_assert(!__builtin_is_implicit_lifetime(_Atomic int));
// expected-error@-1 {{atomic types are not supported in '__builtin_is_implicit_lifetime'}}
static_assert(__builtin_is_implicit_lifetime(int * __restrict));
}

void is_signed()
{
//static_assert(__is_signed(char));
Expand Down

0 comments on commit d213981

Please sign in to comment.