Skip to content

Commit d213981

Browse files
Endillltbaederr
andauthored
[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 <[email protected]>
1 parent d0c16a6 commit d213981

File tree

6 files changed

+213
-1
lines changed

6 files changed

+213
-1
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ The following type trait primitives are supported by Clang. Those traits marked
15471547
* ``__array_extent(type, dim)`` (Embarcadero):
15481548
The ``dim``'th array bound in the type ``type``, or ``0`` if
15491549
``dim >= __array_rank(type)``.
1550+
* ``__builtin_is_implicit_lifetime`` (C++, GNU, Microsoft)
15501551
* ``__builtin_is_virtual_base_of`` (C++, GNU, Microsoft)
15511552
* ``__can_pass_in_regs`` (C++)
15521553
Returns whether a class can be passed in registers under the current

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ C++23 Feature Support
9595
C++2c Feature Support
9696
^^^^^^^^^^^^^^^^^^^^^
9797

98+
- Add ``__builtin_is_implicit_lifetime`` intrinsic, which supports
99+
`P2647R1 A trait for implicit lifetime types <https://wg21.link/p2674r1>`_
100+
98101
- Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
99102
`P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_
100103

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8968,6 +8968,8 @@ def err_atomic_op_has_invalid_synch_scope : Error<
89688968
def warn_atomic_implicit_seq_cst : Warning<
89698969
"implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary">,
89708970
InGroup<DiagGroup<"atomic-implicit-seq-cst">>, DefaultIgnore;
8971+
def err_atomic_unsupported : Error<
8972+
"atomic types are not supported in '%0'">;
89718973

89728974
def err_overflow_builtin_must_be_int : Error<
89738975
"operand argument to %select{overflow builtin|checked integer operation}0 "

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX)
502502
TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX)
503503

504504
// GNU and MS Type Traits
505+
TYPE_TRAIT_1(__builtin_is_implicit_lifetime, IsImplicitLifetime, KEYCXX)
505506
TYPE_TRAIT_2(__builtin_is_virtual_base_of, IsVirtualBaseOf, KEYCXX)
506507
TYPE_TRAIT_1(__has_nothrow_assign, HasNothrowAssign, KEYCXX)
507508
TYPE_TRAIT_1(__has_nothrow_copy, HasNothrowCopy, KEYCXX)

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4948,6 +4948,20 @@ static bool DiagnoseVLAInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
49484948
return true;
49494949
}
49504950

4951+
/// Checks that type T is not an atomic type (_Atomic).
4952+
///
4953+
/// @returns @c true if @p T is VLA and a diagnostic was emitted,
4954+
/// @c false otherwise.
4955+
static bool DiagnoseAtomicInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
4956+
clang::tok::TokenKind TypeTraitID) {
4957+
if (!T->getType()->isAtomicType())
4958+
return false;
4959+
4960+
S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_atomic_unsupported)
4961+
<< TypeTraitID;
4962+
return true;
4963+
}
4964+
49514965
/// Check the completeness of a type in a unary type trait.
49524966
///
49534967
/// If the particular type trait requires a complete type, tries to complete
@@ -5038,6 +5052,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
50385052

50395053
// LWG3823: T shall be an array type, a complete type, or cv void.
50405054
case UTT_IsAggregate:
5055+
case UTT_IsImplicitLifetime:
50415056
if (ArgTy->isArrayType() || ArgTy->isVoidType())
50425057
return true;
50435058

@@ -5634,6 +5649,40 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
56345649
return false;
56355650
case UTT_IsTriviallyEqualityComparable:
56365651
return isTriviallyEqualityComparableType(Self, T, KeyLoc);
5652+
case UTT_IsImplicitLifetime: {
5653+
DiagnoseVLAInCXXTypeTrait(Self, TInfo,
5654+
tok::kw___builtin_is_implicit_lifetime);
5655+
DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
5656+
tok::kw___builtin_is_implicit_lifetime);
5657+
5658+
// [basic.types.general] p9
5659+
// Scalar types, implicit-lifetime class types ([class.prop]),
5660+
// array types, and cv-qualified versions of these types
5661+
// are collectively called implicit-lifetime types.
5662+
QualType UnqualT = T->getCanonicalTypeUnqualified();
5663+
if (UnqualT->isScalarType())
5664+
return true;
5665+
if (UnqualT->isArrayType() || UnqualT->isVectorType())
5666+
return true;
5667+
const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
5668+
if (!RD)
5669+
return false;
5670+
5671+
// [class.prop] p9
5672+
// A class S is an implicit-lifetime class if
5673+
// - it is an aggregate whose destructor is not user-provided or
5674+
// - it has at least one trivial eligible constructor and a trivial,
5675+
// non-deleted destructor.
5676+
const CXXDestructorDecl *Dtor = RD->getDestructor();
5677+
if (UnqualT->isAggregateType())
5678+
if (Dtor && !Dtor->isUserProvided())
5679+
return true;
5680+
if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
5681+
if (RD->hasTrivialDefaultConstructor() ||
5682+
RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
5683+
return true;
5684+
return false;
5685+
}
56375686
}
56385687
}
56395688

clang/test/SemaCXX/type-traits.cpp

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ enum class SignedEnumClass : signed int {};
1818
enum class UnsignedEnumClass : unsigned int {};
1919
struct POD { Enum e; int i; float f; NonPOD* p; };
2020
struct Empty {};
21-
struct IncompleteStruct;
21+
struct IncompleteStruct; // expected-note {{forward declaration of 'IncompleteStruct'}}
2222
typedef Empty EmptyAr[10];
2323
typedef Empty EmptyArNB[];
2424
typedef Empty EmptyArMB[1][2];
@@ -1908,6 +1908,162 @@ void is_pointer_interconvertible_base_of(int n)
19081908
}
19091909
}
19101910

1911+
struct NoEligibleTrivialContructor {
1912+
NoEligibleTrivialContructor() {};
1913+
NoEligibleTrivialContructor(const NoEligibleTrivialContructor&) {}
1914+
NoEligibleTrivialContructor(NoEligibleTrivialContructor&&) {}
1915+
};
1916+
1917+
struct OnlyDefaultConstructorIsTrivial {
1918+
OnlyDefaultConstructorIsTrivial() = default;
1919+
OnlyDefaultConstructorIsTrivial(const OnlyDefaultConstructorIsTrivial&) {}
1920+
OnlyDefaultConstructorIsTrivial(OnlyDefaultConstructorIsTrivial&&) {}
1921+
};
1922+
1923+
struct AllContstructorsAreTrivial {
1924+
AllContstructorsAreTrivial() = default;
1925+
AllContstructorsAreTrivial(const AllContstructorsAreTrivial&) = default;
1926+
AllContstructorsAreTrivial(AllContstructorsAreTrivial&&) = default;
1927+
};
1928+
1929+
struct InheritedNoEligibleTrivialConstructor : NoEligibleTrivialContructor {
1930+
using NoEligibleTrivialContructor::NoEligibleTrivialContructor;
1931+
};
1932+
1933+
struct InheritedOnlyDefaultConstructorIsTrivial : OnlyDefaultConstructorIsTrivial {
1934+
using OnlyDefaultConstructorIsTrivial::OnlyDefaultConstructorIsTrivial;
1935+
};
1936+
1937+
struct InheritedAllContstructorsAreTrivial : AllContstructorsAreTrivial {
1938+
using AllContstructorsAreTrivial::AllContstructorsAreTrivial;
1939+
};
1940+
1941+
struct UserDeclaredDestructor {
1942+
~UserDeclaredDestructor() = default;
1943+
};
1944+
1945+
struct UserProvidedDestructor {
1946+
~UserProvidedDestructor() {}
1947+
};
1948+
1949+
struct UserDeletedDestructorInAggregate {
1950+
~UserDeletedDestructorInAggregate() = delete;
1951+
};
1952+
1953+
struct UserDeletedDestructorInNonAggregate {
1954+
virtual void NonAggregate();
1955+
~UserDeletedDestructorInNonAggregate() = delete;
1956+
};
1957+
1958+
struct DeletedDestructorViaBaseInAggregate : UserDeletedDestructorInAggregate {};
1959+
struct DeletedDestructorViaBaseInNonAggregate : UserDeletedDestructorInNonAggregate {};
1960+
1961+
#if __cplusplus >= 202002L
1962+
template<bool B>
1963+
struct ConstrainedUserDeclaredDefaultConstructor{
1964+
ConstrainedUserDeclaredDefaultConstructor() requires B = default;
1965+
ConstrainedUserDeclaredDefaultConstructor(const ConstrainedUserDeclaredDefaultConstructor&) {}
1966+
};
1967+
1968+
template<bool B>
1969+
struct ConstrainedUserProvidedDestructor {
1970+
~ConstrainedUserProvidedDestructor() = default;
1971+
~ConstrainedUserProvidedDestructor() requires B {}
1972+
};
1973+
#endif
1974+
1975+
struct StructWithFAM {
1976+
int a[];
1977+
};
1978+
1979+
struct StructWithZeroSizedArray {
1980+
int a[0];
1981+
};
1982+
1983+
typedef float float4 __attribute__((ext_vector_type(4)));
1984+
typedef int *align_value_int __attribute__((align_value(16)));
1985+
1986+
struct [[clang::enforce_read_only_placement]] EnforceReadOnlyPlacement {};
1987+
struct [[clang::type_visibility("hidden")]] TypeVisibility {};
1988+
1989+
void is_implicit_lifetime(int n) {
1990+
static_assert(__builtin_is_implicit_lifetime(decltype(nullptr)));
1991+
static_assert(!__builtin_is_implicit_lifetime(void));
1992+
static_assert(!__builtin_is_implicit_lifetime(const void));
1993+
static_assert(!__builtin_is_implicit_lifetime(volatile void));
1994+
static_assert(__builtin_is_implicit_lifetime(int));
1995+
static_assert(!__builtin_is_implicit_lifetime(int&));
1996+
static_assert(!__builtin_is_implicit_lifetime(int&&));
1997+
static_assert(__builtin_is_implicit_lifetime(float));
1998+
static_assert(__builtin_is_implicit_lifetime(double));
1999+
static_assert(__builtin_is_implicit_lifetime(long double));
2000+
static_assert(__builtin_is_implicit_lifetime(int*));
2001+
static_assert(__builtin_is_implicit_lifetime(int[]));
2002+
static_assert(__builtin_is_implicit_lifetime(int[5]));
2003+
static_assert(__builtin_is_implicit_lifetime(int[n]));
2004+
// expected-error@-1 {{variable length arrays are not supported in '__builtin_is_implicit_lifetime'}}
2005+
static_assert(__builtin_is_implicit_lifetime(Enum));
2006+
static_assert(__builtin_is_implicit_lifetime(EnumClass));
2007+
static_assert(!__builtin_is_implicit_lifetime(void()));
2008+
static_assert(!__builtin_is_implicit_lifetime(void() &));
2009+
static_assert(!__builtin_is_implicit_lifetime(void() const));
2010+
static_assert(!__builtin_is_implicit_lifetime(void(&)()));
2011+
static_assert(__builtin_is_implicit_lifetime(void(*)()));
2012+
static_assert(__builtin_is_implicit_lifetime(decltype(nullptr)));
2013+
static_assert(__builtin_is_implicit_lifetime(int UserDeclaredDestructor::*));
2014+
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)()));
2015+
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() const));
2016+
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &));
2017+
static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &&));
2018+
static_assert(!__builtin_is_implicit_lifetime(IncompleteStruct));
2019+
// expected-error@-1 {{incomplete type 'IncompleteStruct' used in type trait expression}}
2020+
static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[]));
2021+
static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[5]));
2022+
static_assert(__builtin_is_implicit_lifetime(UserDeclaredDestructor));
2023+
static_assert(__builtin_is_implicit_lifetime(const UserDeclaredDestructor));
2024+
static_assert(__builtin_is_implicit_lifetime(volatile UserDeclaredDestructor));
2025+
static_assert(!__builtin_is_implicit_lifetime(UserProvidedDestructor));
2026+
static_assert(!__builtin_is_implicit_lifetime(NoEligibleTrivialContructor));
2027+
static_assert(__builtin_is_implicit_lifetime(OnlyDefaultConstructorIsTrivial));
2028+
static_assert(__builtin_is_implicit_lifetime(AllContstructorsAreTrivial));
2029+
static_assert(!__builtin_is_implicit_lifetime(InheritedNoEligibleTrivialConstructor));
2030+
static_assert(__builtin_is_implicit_lifetime(InheritedOnlyDefaultConstructorIsTrivial));
2031+
static_assert(__builtin_is_implicit_lifetime(InheritedAllContstructorsAreTrivial));
2032+
static_assert(__builtin_is_implicit_lifetime(UserDeletedDestructorInAggregate));
2033+
static_assert(!__builtin_is_implicit_lifetime(UserDeletedDestructorInNonAggregate));
2034+
static_assert(__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInAggregate) == __cplusplus >= 201703L);
2035+
static_assert(!__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInNonAggregate));
2036+
#if __cplusplus >= 202002L
2037+
static_assert(__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<true>));
2038+
static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<false>));
2039+
static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<true>));
2040+
static_assert(__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<false>));
2041+
#endif
2042+
2043+
static_assert(__builtin_is_implicit_lifetime(__int128));
2044+
static_assert(__builtin_is_implicit_lifetime(_BitInt(8)));
2045+
static_assert(__builtin_is_implicit_lifetime(_BitInt(128)));
2046+
static_assert(__builtin_is_implicit_lifetime(int[0]));
2047+
static_assert(__builtin_is_implicit_lifetime(StructWithFAM));
2048+
static_assert(__builtin_is_implicit_lifetime(StructWithZeroSizedArray));
2049+
static_assert(__builtin_is_implicit_lifetime(__fp16));
2050+
static_assert(__builtin_is_implicit_lifetime(__bf16));
2051+
static_assert(__builtin_is_implicit_lifetime(_Complex double));
2052+
static_assert(__builtin_is_implicit_lifetime(float4));
2053+
static_assert(__builtin_is_implicit_lifetime(align_value_int));
2054+
static_assert(__builtin_is_implicit_lifetime(int[[clang::annotate_type("category2")]] *));
2055+
static_assert(__builtin_is_implicit_lifetime(int __attribute__((btf_type_tag("user"))) *));
2056+
static_assert(__builtin_is_implicit_lifetime(EnforceReadOnlyPlacement));
2057+
static_assert(__builtin_is_implicit_lifetime(int __attribute__((noderef)) *));
2058+
static_assert(__builtin_is_implicit_lifetime(TypeVisibility));
2059+
static_assert(__builtin_is_implicit_lifetime(int * _Nonnull));
2060+
static_assert(__builtin_is_implicit_lifetime(int * _Null_unspecified));
2061+
static_assert(__builtin_is_implicit_lifetime(int * _Nullable));
2062+
static_assert(!__builtin_is_implicit_lifetime(_Atomic int));
2063+
// expected-error@-1 {{atomic types are not supported in '__builtin_is_implicit_lifetime'}}
2064+
static_assert(__builtin_is_implicit_lifetime(int * __restrict));
2065+
}
2066+
19112067
void is_signed()
19122068
{
19132069
//static_assert(__is_signed(char));

0 commit comments

Comments
 (0)