https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/101807
>From 9c4e7ccf47d5ede2b6169effb2a09668f512a182 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Sat, 3 Aug 2024 13:05:21 +0300 Subject: [PATCH 1/6] [clang] Implement `__builtin_is_implicit_lifetime()` This intrinsic supports [P2647R1](https://wg21.link/p2674r1) "A trait for implicit lifetime types". --- clang/docs/LanguageExtensions.rst | 1 + clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/TokenKinds.def | 1 + clang/lib/Sema/SemaExprCXX.cpp | 22 +++++++ clang/test/SemaCXX/type-traits.cpp | 81 +++++++++++++++++++++++- 5 files changed, 107 insertions(+), 1 deletion(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a747464582e77d..f04e6b0057b512 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -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 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 25f5bd37bbe94f..6854a321e17206 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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>`_ diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 7e638dc1ddcdba..7505c5a1a1f27c 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index c5003d9ac02549..504dc93316db50 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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; @@ -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; + + CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); + if (!RD) + return false; + if (UnqualT->isAggregateType()) + if (!RD->getDestructor()->isUserProvided()) + return true; + if (RD->hasTrivialDestructor()) + if (RD->hasTrivialDefaultConstructor() || + RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor()) + return true; + return false; + } } } diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 4acb3d6c9eebea..11041414c1bbab 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -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]; @@ -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)); >From ad0abb5ae0332c4de83723a96e3f765891993dc5 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Sat, 3 Aug 2024 16:38:08 +0400 Subject: [PATCH 2/6] Apply Timm's suggestion Co-authored-by: Timm Baeder <tbae...@redhat.com> --- clang/lib/Sema/SemaExprCXX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 504dc93316db50..6f7e4203a17142 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5647,7 +5647,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, if (UnqualT->isArrayType()) return true; - CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); + const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); if (!RD) return false; if (UnqualT->isAggregateType()) >From 49de16a0126a3fe3284f017ee4a4969d41bb4094 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Sun, 11 Aug 2024 21:46:58 +0300 Subject: [PATCH 3/6] Address feedback --- clang/lib/Sema/SemaExprCXX.cpp | 17 ++++++++++++--- clang/test/SemaCXX/type-traits.cpp | 34 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 6f7e4203a17142..5fb735600fac5a 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5641,19 +5641,30 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, case UTT_IsImplicitLifetime: { DiagnoseVLAInCXXTypeTrait(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()) 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 (!RD->getDestructor()->isUserProvided()) + if (Dtor && !Dtor->isUserProvided()) return true; - if (RD->hasTrivialDestructor()) + if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted())) if (RD->hasTrivialDefaultConstructor() || RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor()) return true; diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 11041414c1bbab..fea6bec7476783 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -1982,6 +1982,31 @@ 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() requires B {} +}; +#endif + void is_implicit_lifetime(int n) { static_assert(!__builtin_is_implicit_lifetime(void)); static_assert(!__builtin_is_implicit_lifetime(const void)); @@ -2021,6 +2046,15 @@ void is_implicit_lifetime(int n) { 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>)); +#endif } void is_signed() >From 5dbfdf514503c45c7071348125c809b6ef13bcce Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Sun, 11 Aug 2024 21:55:03 +0300 Subject: [PATCH 4/6] Remove whitespaces --- clang/lib/Sema/SemaExprCXX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 5fb735600fac5a..17f21f1e650732 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5641,7 +5641,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, case UTT_IsImplicitLifetime: { DiagnoseVLAInCXXTypeTrait(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 @@ -5654,7 +5654,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, 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 >From 5221a00354b294289323151c462c83b585e80656 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Tue, 13 Aug 2024 11:54:41 +0300 Subject: [PATCH 5/6] Handle vector types, add tests for various extensions --- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/test/SemaCXX/type-traits.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 17f21f1e650732..d8bca391173749 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5649,7 +5649,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, QualType UnqualT = T->getCanonicalTypeUnqualified(); if (UnqualT->isScalarType()) return true; - if (UnqualT->isArrayType()) + if (UnqualT->isArrayType() || UnqualT->isVectorType()) return true; const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); if (!RD) diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index fea6bec7476783..1cd34c263d495e 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -2007,6 +2007,17 @@ struct ConstrainedUserProvidedDestructor { }; #endif +struct StructWithFAM { + int a[]; +}; + +struct StructWithZeroSizedArray { + int a[0]; +}; + +typedef float float4 __attribute__((vector_type(4))); +typedef float ext_float4 __attribute__((ext_vector_type(4))); + void is_implicit_lifetime(int n) { static_assert(!__builtin_is_implicit_lifetime(void)); static_assert(!__builtin_is_implicit_lifetime(const void)); @@ -2014,6 +2025,9 @@ void is_implicit_lifetime(int n) { 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])); @@ -2055,6 +2069,18 @@ void is_implicit_lifetime(int n) { static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<false>)); static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<true>)); #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(ext_float4)); } void is_signed() >From 318cc3bd41f3cbf022805874edaacd421dc4de5a Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Tue, 13 Aug 2024 13:40:31 +0300 Subject: [PATCH 6/6] So `vector_type` is not a thing --- clang/test/SemaCXX/type-traits.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 1cd34c263d495e..22a4e3f02e764c 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -2015,8 +2015,7 @@ struct StructWithZeroSizedArray { int a[0]; }; -typedef float float4 __attribute__((vector_type(4))); -typedef float ext_float4 __attribute__((ext_vector_type(4))); +typedef float float4 __attribute__((ext_vector_type(4))); void is_implicit_lifetime(int n) { static_assert(!__builtin_is_implicit_lifetime(void)); @@ -2080,7 +2079,6 @@ void is_implicit_lifetime(int n) { 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(ext_float4)); } void is_signed() _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits