https://github.com/cjdb created https://github.com/llvm/llvm-project/pull/238456
This provides a canonical way to determine a type's destructor information for both the standard library and projects using Clang as a library. **Alternatives considered** * **Make `EvaluateUnaryTypeTrait` a public function.** Not chosen because it isn't clear whether consumers can obtain obtain `TypeSourceInfo`. This alternative is more viable if Clang provides a way for users to source this information without difficulty. * **Combine `GetDestructorFormat` and `IsDestructorNothrow`.** Not chosen because trivial destructors can be marked as potentially-throwing, so we'd need to add `DestructorFormat::TrivialThrowing` and `DestructorFormat::NonTrivialThrowing`. Checking that the destructor is non-throwing non-throwing is also orthogonal to checking that that the type is destructible. Checking this property only when necessary is also a separation of concerns. * **Split `GetDestructorFormat` into a Boolean function for each trait.** This approach requires either a non-trivial amount of code duplication, or creating helpers that don't fit together very well. It also limits what a user can do, since Boolean function only returns one bit of information at a time (we can use `GetDestructorFormat` in a switch-statement, for example). * **Replace `NotAvailable` with more nuanced enumerators (e.g. `Deleted`, `Private`, etc.).** Not chosen because there isn't a clear use-case for this at the moment, and because that functionality can be queried in other ways. >From 058eab4922b0811166e4c71046d4fb8d722c9029 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Tue, 3 Nov 2026 01:39:02 +0000 Subject: [PATCH] Moves destructor type-trait deduction to a public function This provides a canonical way to determine a type's destructor information for both the standard library and projects using Clang as a library. --- clang/include/clang/Sema/Sema.h | 32 +++++++++ clang/lib/Sema/SemaTypeTraits.cpp | 115 ++++++++++++++++++------------ 2 files changed, 102 insertions(+), 45 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ba215e7e18d7ba..c863e6cdf3406b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -16057,6 +16057,38 @@ class Sema final : public SemaBase { ///@} }; +enum class DestructorFormat { + // SMF is deleted, private, protected, or otherwise inaccessible. + NotAvailable, + + // SMF is trivial. + Trivial, + + // SMF is non-trivial and nothrow. + NonTrivial, +}; + +/// Returns whether a type is trivially-destructible, non-trivially +/// destructible, or cannot be destroyed in the current context. +/// +/// It is possible for a type to have a trivial destructor without being +/// trivially-destructible. For example, `struct S { ~S() = delete; };` +/// has a trivial destructor: its destructor is non-virtual, all of its +/// bases have trivial destructors, and all of its members are trivially +/// destructible. `S` isn't trivially-destructible because its destructor +/// has been deleted. Similarly, `class C { ~C() = default; }` isn't +/// trivially-destructible because `~C()`---although trivial for the +/// same reasons---is private. +/// +/// This would ideally be placed in `QualType`, but libClangBasic +/// doesn't include `Sema`. +DestructorFormat GetDestructorFormat(Sema &S, const ASTContext &C, + SourceLocation Loc, QualType T); + +/// Returns whether a type's destructor is nothrow. +bool IsDestructorNoThrow(Sema &S, const ASTContext &C, SourceLocation Loc, + QualType T); + DeductionFailureInfo MakeDeductionFailureInfo(ASTContext &Context, TemplateDeductionResult TDK, sema::TemplateDeductionInfo &Info); diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index cb30ff9fd02da9..91328c09750e8d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -653,6 +653,67 @@ static ComparisonCategoryResult EvaluateTypeOrder(Sema &S, QualType LHS, : ComparisonCategoryResult::Less; } +DestructorFormat clang::GetDestructorFormat(Sema &S, const ASTContext &C, + SourceLocation Loc, QualType T) { + // C++14 [meta.unary.prop]: + // For reference types, is_destructible<T>::value is true. + if (T->isReferenceType()) + return DestructorFormat::Trivial; + + // Objective-C++ ARC: autorelease types don't require destruction. + if (T->isObjCLifetimeType() && + T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) + return DestructorFormat::Trivial; + + // C++14 [meta.unary.prop]: + // For incomplete types and function types, is_destructible<T>::value is + // false. + if (T->isIncompleteType() || T->isFunctionType()) + return DestructorFormat::NotAvailable; + + DestructorFormat Result = DestructorFormat::Trivial; + + // A type that requires destruction (via a non-trivial destructor or ARC + // lifetime semantics) is not trivially-destructible. + if (T.isDestructedType()) + Result = DestructorFormat::NonTrivial; + + // C++14 [meta.unary.prop]: + // For object types and given U equal to remove_all_extents_t<T>, if the + // expression std::declval<U&>().~U() is well-formed when treated as an + // unevaluated operand (Clause 5), then is_destructible<T>::value is true + auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl(); + if (!RD) + return Result; + + CXXDestructorDecl *Destructor = S.LookupDestructor(RD); + if (!Destructor) + return DestructorFormat::NotAvailable; + + // C++14 [dcl.fct.def.delete]p2: + // A program that refers to a deleted function implicitly or + // explicitly, other than to declare it, is ill-formed. + if (Destructor->isDeleted()) + return DestructorFormat::NotAvailable; + + if (C.getLangOpts().AccessControl && Destructor->getAccess() != AS_public) + return DestructorFormat::NotAvailable; + + return Result; +} + +bool clang::IsDestructorNoThrow(Sema &S, const ASTContext &C, + SourceLocation Loc, QualType T) { + auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl(); + if (!RD) + return true; + + CXXDestructorDecl *Destructor = S.LookupDestructor(RD); + auto *CPT = Destructor->getType()->castAs<FunctionProtoType>(); + CPT = S.ResolveExceptionSpec(Loc, CPT); + return CPT && CPT->isNothrow(); +} + static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, SourceLocation KeyLoc, TypeSourceInfo *TInfo) { @@ -878,52 +939,16 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, !RD->hasNonTrivialCopyAssignment(); return false; case UTT_IsDestructible: + return clang::GetDestructorFormat(Self, C, KeyLoc, T) != + DestructorFormat::NotAvailable; case UTT_IsTriviallyDestructible: - case UTT_IsNothrowDestructible: - // C++14 [meta.unary.prop]: - // For reference types, is_destructible<T>::value is true. - if (T->isReferenceType()) - return true; - - // Objective-C++ ARC: autorelease types don't require destruction. - if (T->isObjCLifetimeType() && - T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) - return true; - - // C++14 [meta.unary.prop]: - // For incomplete types and function types, is_destructible<T>::value is - // false. - if (T->isIncompleteType() || T->isFunctionType()) - return false; - - // A type that requires destruction (via a non-trivial destructor or ARC - // lifetime semantics) is not trivially-destructible. - if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType()) - return false; - - // C++14 [meta.unary.prop]: - // For object types and given U equal to remove_all_extents_t<T>, if the - // expression std::declval<U&>().~U() is well-formed when treated as an - // unevaluated operand (Clause 5), then is_destructible<T>::value is true - if (auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) { - CXXDestructorDecl *Destructor = Self.LookupDestructor(RD); - if (!Destructor) - return false; - // C++14 [dcl.fct.def.delete]p2: - // A program that refers to a deleted function implicitly or - // explicitly, other than to declare it, is ill-formed. - if (Destructor->isDeleted()) - return false; - if (C.getLangOpts().AccessControl && Destructor->getAccess() != AS_public) - return false; - if (UTT == UTT_IsNothrowDestructible) { - auto *CPT = Destructor->getType()->castAs<FunctionProtoType>(); - CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); - if (!CPT || !CPT->isNothrow()) - return false; - } - } - return true; + return clang::GetDestructorFormat(Self, C, KeyLoc, T) == + DestructorFormat::Trivial; + case UTT_IsNothrowDestructible: { + auto Format = clang::GetDestructorFormat(Self, C, KeyLoc, T); + return Format != DestructorFormat::NotAvailable && + clang::IsDestructorNoThrow(Self, C, KeyLoc, T); + } case UTT_HasTrivialDestructor: // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
