[clang] d213981 - [clang] Implement `__builtin_is_implicit_lifetime()` (#101807)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 14 09:16:00 PDT 2024
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 <tbaeder at 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 at -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 at -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 at -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));
More information about the cfe-commits
mailing list