https://github.com/AnushaK6 created https://github.com/llvm/llvm-project/pull/167291
This patch adds diagnostic reasoning for the std::is_destructible type trait in Clang’s Sema diagnostics, in response to issue #141911 It enables detailed “unsatisfied trait” messages when a type fails destructibility checks (e.g., void, function types, deleted/private destructors, incomplete types, etc). Changes: - Added DiagnoseNonDestructibleReason() to SemaTypeTraits.cpp - Integrated UTT_IsDestructible handling in Sema::DiagnoseTypeTraitDetails - Updated diagnostic notes in DiagnosticSemaKinds.td Added new tests to: - clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp - clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp >From e1e0476c4d36b3213ce180d28ba224f69173c86a Mon Sep 17 00:00:00 2001 From: AnushaK6 <[email protected]> Date: Mon, 10 Nov 2025 14:30:42 +0530 Subject: [PATCH] Add diagnostic reasoning for unsatisfied is_destructible trait --- .../clang/Basic/DiagnosticSemaKinds.td | 8 +- clang/lib/Sema/SemaTypeTraits.cpp | 64 ++++++++++++++++ .../type-traits-unsatisfied-diags-std.cpp | 76 +++++++++++++++++++ .../SemaCXX/type-traits-unsatisfied-diags.cpp | 49 ++++++++++++ 4 files changed, 193 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1..8fb76c2c989f5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1776,7 +1776,8 @@ def note_unsatisfied_trait "%StandardLayout{standard-layout}|" "%Aggregate{aggregate}|" "%Final{final}|" - "%Abstract{abstract}" + "%Abstract{abstract}|" + "%Destructible{destructible}" "}1">; def note_unsatisfied_trait_reason @@ -1808,6 +1809,7 @@ def note_unsatisfied_trait_reason "%NonStandardLayoutMember{has a non-standard-layout member %1 of type %2}|" "%IndirectBaseWithFields{has an indirect base %1 with data members}|" "%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|" + "%InaccessibleDtr{has a %select{private|protected}1 destructor}|" "%UserProvidedCtr{has a user provided %select{copy|move}1 " "constructor}|" "%UserDeclaredCtr{has a user-declared constructor}|" @@ -1823,6 +1825,7 @@ def note_unsatisfied_trait_reason "%FunctionType{is a function type}|" "%CVVoidType{is a cv void type}|" "%IncompleteArrayType{is an incomplete array type}|" + "%IncompleteType{is an incomplete type}|" "%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|" "%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|" "%NotClassOrUnion{is not a class or union type}|" @@ -12137,9 +12140,6 @@ def err_omp_unexpected_schedule_modifier : Error< "modifier '%0' cannot be used along with modifier '%1'">; def err_omp_schedule_nonmonotonic_static : Error< "'nonmonotonic' modifier can only be specified with 'dynamic' or 'guided' schedule kind">; -def err_omp_incompatible_dyn_groupprivate_modifier - : Error<"modifier '%0' cannot be used along with modifier '%1' in " - "dyn_groupprivate">; def err_omp_simple_clause_incompatible_with_ordered : Error< "'%0' clause with '%1' modifier cannot be specified if an 'ordered' clause is specified">; def err_omp_ordered_simd : Error< diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 38877967af05e..fef47dfc2cc51 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2028,6 +2028,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) { .Case("is_constructible", TypeTrait::TT_IsConstructible) .Case("is_final", TypeTrait::UTT_IsFinal) .Case("is_abstract", TypeTrait::UTT_IsAbstract) + .Case("is_destructible", TypeTrait::UTT_IsDestructible) .Default(std::nullopt); } @@ -2399,6 +2400,66 @@ static void DiagnoseNonConstructibleReason( SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; } +static void DiagnoseNonDestructibleReason(Sema &SemaRef, SourceLocation Loc, + QualType T) { + + QualType CoreT = T.getCanonicalType(); + if (const ArrayType *AT = SemaRef.Context.getAsArrayType(CoreT)) + CoreT = AT->getElementType(); + + SemaRef.Diag(Loc, diag::note_unsatisfied_trait) + << CoreT << diag::TraitName::Destructible; + + if (CoreT->isFunctionType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::FunctionType; + return; + } + + if (CoreT->isVoidType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::CVVoidType; + return; + } + + if (CoreT->isIncompleteType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::IncompleteType; + return; + } + + const CXXRecordDecl *RD = CoreT->getAsCXXRecordDecl(); + if (!RD || RD->isInvalidDecl()) + return; + + const CXXRecordDecl *Def = RD->getDefinition(); + if (!Def) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::IncompleteType; + return; + } + + CXXDestructorDecl *Dtor = Def->getDestructor(); + if (!Dtor) + return; + + if (Dtor->isDeleted()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::DeletedDtr << 0 + << Dtor->getSourceRange(); + return; + } + + AccessSpecifier AS = Dtor->getAccess(); + if (AS == AS_private || AS == AS_protected) { + unsigned Select = (AS == AS_private) ? 0 : 1; + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::InaccessibleDtr << Select + << Dtor->getSourceRange(); + return; + } +} + static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, SourceLocation Loc, QualType T) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait) @@ -2889,6 +2950,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) { case TT_IsConstructible: DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args); break; + case UTT_IsDestructible: + DiagnoseNonDestructibleReason(*this, E->getBeginLoc(), Args[0]); + break; case UTT_IsAggregate: DiagnoseNonAggregateReason(*this, E->getBeginLoc(), Args[0]); break; diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp index 3e03a79275232..3e02fe8f10f56 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp @@ -73,6 +73,15 @@ struct is_abstract { template <typename T> constexpr bool is_abstract_v = __is_abstract(T); +template <typename T> +struct is_destructible { + static constexpr bool value = __is_destructible(T); +}; + +template <typename T> +constexpr bool is_destructible_v = __is_destructible(T); + + #endif #ifdef STD2 @@ -167,6 +176,17 @@ using is_abstract = __details_is_abstract<T>; template <typename T> constexpr bool is_abstract_v = __is_abstract(T); +template <typename T> +struct __details_is_destructible { + static constexpr bool value = __is_destructible(T); +}; + +template <typename T> +using is_destructible = __details_is_destructible<T>; + +template <typename T> +constexpr bool is_destructible_v = __is_destructible(T); + #endif @@ -252,6 +272,15 @@ using is_abstract = __details_is_abstract<T>; template <typename T> constexpr bool is_abstract_v = is_abstract<T>::value; +template <typename T> +struct __details_is_destructible : bool_constant<__is_destructible(T)> {}; + +template <typename T> +using is_destructible = __details_is_destructible<T>; + +template <typename T> +constexpr bool is_destructible_v = is_destructible<T>::value; + #endif } @@ -374,6 +403,18 @@ static_assert(std::is_abstract_v<int&>); // expected-note@-1 {{because it is a reference type}} \ // expected-note@-1 {{because it is not a struct or class type}} +static_assert(std::is_destructible<int>::value); + +static_assert(std::is_destructible<void>::value); +// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_destructible<void>::value'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + +static_assert(std::is_destructible_v<void>); +// expected-error@-1 {{static assertion failed due to requirement 'std::is_destructible_v<void>'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + namespace test_namespace { using namespace std; @@ -473,6 +514,17 @@ namespace test_namespace { // expected-note@-1 {{'int &' is not abstract}} \ // expected-note@-1 {{because it is a reference type}} \ // expected-note@-1 {{because it is not a struct or class type}} + + static_assert(is_destructible<void>::value); + // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_destructible<void>::value'}} \ + // expected-note@-1 {{'void' is not destructible}} \ + // expected-note@-1 {{because it is a cv void type}} + + static_assert(is_destructible_v<void>); + // expected-error@-1 {{static assertion failed due to requirement 'is_destructible_v<void>'}} \ + // expected-note@-1 {{'void' is not destructible}} \ + // expected-note@-1 {{because it is a cv void type}} + } @@ -518,6 +570,15 @@ concept C5 = std::is_aggregate_v<T>; // #concept10 template <C5 T> void g5(); // #cand10 +template <typename T> +requires std::is_destructible<T>::value void f6(); // #cand11 + +template <typename T> +concept C6 = std::is_destructible_v<T>; // #concept11 + +template <C6 T> void g6(); // #cand12 + + void test() { f<int&>(); // expected-error@-1 {{no matching function for call to 'f'}} \ @@ -589,6 +650,21 @@ void test() { // expected-note@#concept10 {{because 'std::is_aggregate_v<void>' evaluated to false}} \ // expected-note@#concept10 {{'void' is not aggregate}} \ // expected-note@#concept10 {{because it is a cv void type}} + + f6<void>(); + // expected-error@-1 {{no matching function for call to 'f6'}} \ + // expected-note@#cand11 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note-re@#cand11 {{because '{{.*}}is_destructible<void>::value' evaluated to false}} \ + // expected-note@#cand11 {{'void' is not destructible}} \ + // expected-note@#cand11 {{because it is a cv void type}} + + g6<void>(); + // expected-error@-1 {{no matching function for call to 'g6'}} \ + // expected-note@#cand12 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note@#cand12 {{because 'void' does not satisfy 'C6'}} \ + // expected-note@#concept11 {{because 'std::is_destructible_v<void>' evaluated to false}} \ + // expected-note@#concept11 {{'void' is not destructible}} \ + // expected-note@#concept11 {{because it is a cv void type}} } } diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp index 22740418f09f5..858a5cc24868f 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp @@ -1052,3 +1052,52 @@ static_assert(__is_abstract(U)); // expected-note@-1 {{because it is not a struct or class type}} } +namespace destructible { + +struct Incomplete; // expected-note {{forward declaration of 'destructible::Incomplete'}} +static_assert(__is_destructible(Incomplete)); +// expected-error@-1 {{incomplete type 'Incomplete' used in type trait expression}} + +static_assert(__is_destructible(void)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void)'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + +using F = void(); +static_assert(__is_destructible(F)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void ())'}} \ +// expected-note@-1 {{'void ()' is not destructible}} \ +// expected-note@-1 {{because it is a function type}} + +using Ref = int&; +static_assert(__is_destructible(Ref)); // no diagnostics (true) + +struct DeletedDtor { // #d-DeletedDtor + ~DeletedDtor() = delete; +}; +static_assert(__is_destructible(DeletedDtor)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DeletedDtor)'}} \ +// expected-note@-1 {{'destructible::DeletedDtor' is not destructible}} \ +// expected-note@-1 {{because it has a deleted destructor}} + +struct PrivateDtor { // #d-PrivateDtor +private: + ~PrivateDtor(); // #d-PrivateDtor-dtor +}; +static_assert(__is_destructible(PrivateDtor)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::PrivateDtor)'}} \ +// expected-note@-1 {{'destructible::PrivateDtor' is not destructible}} \ +// expected-note@-1 {{because it has a private destructor}} + +struct BaseInaccessible { // #d-BaseInacc +private: + ~BaseInaccessible(); // #d-BaseInacc-dtor +}; + +struct DerivedFromInaccessible : BaseInaccessible {}; // #d-DerivedInacc +static_assert(__is_destructible(DerivedFromInaccessible)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DerivedFromInaccessible)'}} \ +// expected-note@-1 {{'destructible::DerivedFromInaccessible' is not destructible}} \ +// expected-note@-1 {{because it has a deleted destructor}} + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
