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 2 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
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
22 changes: 22 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5039,6 +5039,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 +5638,27 @@ 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);
QualType UnqualT = T->getCanonicalTypeUnqualified();
if (UnqualT->isScalarType())
return true;
if (UnqualT->isArrayType())
return true;

const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
if (!RD)
return false;
if (UnqualT->isAggregateType())
if (!RD->getDestructor()->isUserProvided())
return true;
if (RD->hasTrivialDestructor())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to check that it's not deleted too.

Currently the first two assertions fail here:

struct X { ~X() = delete; };
struct Y { X x; ~Y() = default; };
struct Z { X x; };

static_assert(!__builtin_is_implicit_lifetime(X));
static_assert(!__builtin_is_implicit_lifetime(Y));
static_assert( __builtin_is_implicit_lifetime(Z));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three types in your examples are implicit lifetime class types, because they are aggregates and does not have a user-provided destructor.

Copy link
Contributor

@MitalAshok MitalAshok Aug 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct for Z but X and Y are not aggregates because they have user-declared destructors, so they need a non-deleted destructor. I've misread something, this appears to be correct

Copy link
Contributor Author

@Endilll Endilll Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but X and Y are not aggregates because they have user-declared constructors

I'm sorry, but the only thing I see there are user-declared destructors, not constructors.

if (RD->hasTrivialDefaultConstructor() ||
RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
return true;
return false;
}
}
}

Expand Down
81 changes: 80 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,85 @@ 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() {}
};

void is_implicit_lifetime(int n) {
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(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));
}

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