https://github.com/AnushaK6 created 
https://github.com/llvm/llvm-project/pull/166967

This patch adds diagnostic reasoning for the std::is_destructible type trait in 
Clang’s Sema diagnostics.
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 14182fb64adde6e86a96f7a6ea0c22749124b827 Mon Sep 17 00:00:00 2001
From: AnushaK6 <[email protected]>
Date: Fri, 7 Nov 2025 22:33:04 +0530
Subject: [PATCH 1/2] Add diagnostic reasoning for unsatisfied is_destructible
 trait

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 +-
 clang/lib/Sema/SemaTypeTraits.cpp             | 62 +++++++++++++++
 .../type-traits-unsatisfied-diags-std.cpp     | 76 +++++++++++++++++++
 .../SemaCXX/type-traits-unsatisfied-diags.cpp | 49 ++++++++++++
 4 files changed, 191 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4e369be0bbb92..ee357936e3a87 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}|"
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index 38877967af05e..e9b8032733efc 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,64 @@ 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 +2948,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}}
+
+}

>From 5b5bdbfeafceedec79b173bf8e1d317818d7127d Mon Sep 17 00:00:00 2001
From: AnushaK6 <[email protected]>
Date: Fri, 7 Nov 2025 22:34:51 +0530
Subject: [PATCH 2/2] [NFC] Format code using clang-format

---
 clang/lib/Sema/SemaTypeTraits.cpp | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index e9b8032733efc..fef47dfc2cc51 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2400,29 +2400,31 @@ static void DiagnoseNonConstructibleReason(
   SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
 }
 
-static void DiagnoseNonDestructibleReason(
-    Sema &SemaRef, SourceLocation Loc,
-    QualType T) {
+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;
-
+  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;
+  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;
+
+  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;
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::IncompleteType;
     return;
   }
 
@@ -2433,7 +2435,7 @@ static void DiagnoseNonDestructibleReason(
   const CXXRecordDecl *Def = RD->getDefinition();
   if (!Def) {
     SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
-      << diag::TraitNotSatisfiedReason::IncompleteType;
+        << diag::TraitNotSatisfiedReason::IncompleteType;
     return;
   }
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to