Author: Vlad Serebrennikov Date: 2024-08-14T20:15:56+04:00 New Revision: d213981c80626698a07b11ce872acba098a863d4
URL: https://github.com/llvm/llvm-project/commit/d213981c80626698a07b11ce872acba098a863d4 DIFF: https://github.com/llvm/llvm-project/commit/d213981c80626698a07b11ce872acba098a863d4.diff LOG: [clang] Implement `__builtin_is_implicit_lifetime()` (#101807) This intrinsic supports [P2647R1](https://wg21.link/p2674r1) "A trait for implicit lifetime types". Resolves #98627 --------- Co-authored-by: Timm Baeder <tbae...@redhat.com> Added: Modified: clang/docs/LanguageExtensions.rst clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/TokenKinds.def clang/lib/Sema/SemaExprCXX.cpp clang/test/SemaCXX/type-traits.cpp Removed: ################################################################################ diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index be07f81cc41b00..4679dbb68b25e1 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -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 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 007ccdf49dea19..45cff185130af5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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>`_ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 554dbaff2ce0d8..8d4f8f36d8565b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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 " diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 421dbb413fed93..d683106bb0e298 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 124435330ca104..5356bcf172f752 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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 @@ -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; @@ -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; + } } } diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index e5d2ced3caaed5..bf069d9bc082c3 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]; @@ -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)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits