Skip to content

[clang] Implement __builtin_is_implicit_lifetime() #101807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,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 @@ -86,6 +86,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 @@ -8965,6 +8965,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 @@ -501,6 +501,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 @@ -5039,6 +5053,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 @@ -5637,6 +5652,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 @@ -1944,6 +1944,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
Loading