https://github.com/sebproell created 
https://github.com/llvm/llvm-project/pull/156199

Adds onto #141911 

>From d86aa58c30c784e99cc28cd2b90bd0d667b78ab6 Mon Sep 17 00:00:00 2001
From: Sebastian Proell <sebastian.pro...@tum.de>
Date: Sat, 30 Aug 2025 14:46:25 +0200
Subject: [PATCH] [Clang] Add diagnostic for why std::is_abstract is false

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  8 +-
 clang/lib/Sema/SemaTypeTraits.cpp             | 64 ++++++++++++++++
 .../type-traits-unsatisfied-diags-std.cpp     | 51 +++++++++++++
 .../SemaCXX/type-traits-unsatisfied-diags.cpp | 76 +++++++++++++++++++
 4 files changed, 197 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c934fed2c7462..36e7b66ea1fb0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1773,7 +1773,8 @@ def note_unsatisfied_trait
            "%TriviallyCopyable{trivially copyable}|"
            "%Empty{empty}|"
            "%StandardLayout{standard-layout}|"
-           "%Final{final}"
+           "%Final{final}|"
+           "%Abstract{abstract}"
            "}1">;
 
 def note_unsatisfied_trait_reason
@@ -1818,7 +1819,10 @@ def note_unsatisfied_trait_reason
            "%CVVoidType{is a cv void type}|"
            "%IncompleteArrayType{is an incomplete array type}|"
            "%NotClassOrUnion{is not a class or union type}|"
-           "%NotMarkedFinal{is not marked 'final'}"
+           "%NotMarkedFinal{is not marked 'final'}|"
+           "%UnionType{is a union type}|"
+           "%NotStructOrClass{is not a struct or class type}|"
+           "%OverridesAllPureVirtual{overrides all pure virtual functions from 
base class %1}"
            "}0">;
 
 def warn_consteval_if_always_true : Warning<
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index 37552331478f1..7a2b7a67e57c7 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1966,6 +1966,7 @@ static std::optional<TypeTrait> 
StdNameToTypeTrait(StringRef Name) {
       .Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
       .Case("is_constructible", TypeTrait::TT_IsConstructible)
       .Case("is_final", TypeTrait::UTT_IsFinal)
+      .Case("is_abstract", TypeTrait::UTT_IsAbstract)
       .Default(std::nullopt);
 }
 
@@ -2640,6 +2641,66 @@ static void DiagnoseNonStandardLayoutReason(Sema 
&SemaRef, SourceLocation Loc,
   SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
 }
 
+static void DiagnoseNonAbstractReason(Sema &SemaRef, SourceLocation Loc,
+                                      const CXXRecordDecl *D) {
+  // If this type has any abstract base classes, their respective virtual
+  // functions must have been overridden.
+  for (const CXXBaseSpecifier &B : D->bases()) {
+    assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
+    if (B.getType()->getAsCXXRecordDecl()->isAbstract()) {
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::OverridesAllPureVirtual
+          << B.getType() << B.getSourceRange();
+    }
+  }
+}
+
+static void DiagnoseNonAbstractReason(Sema &SemaRef, SourceLocation Loc,
+                                      QualType T) {
+  SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+      << T << diag::TraitName::Abstract;
+
+  if (T->isReferenceType()) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::Ref;
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::NotStructOrClass;
+    return;
+  }
+
+  if (T->isUnionType()) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::UnionType;
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::NotStructOrClass;
+    return;
+  }
+
+  if (SemaRef.Context.getAsArrayType(T)) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::NotStructOrClass;
+    return;
+  }
+
+  if (T->isFunctionType()) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::FunctionType;
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::NotStructOrClass;
+    return;
+  }
+
+  if (!T->isStructureOrClassType()) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::NotStructOrClass;
+    return;
+  }
+
+  const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+  if (D->hasDefinition())
+    DiagnoseNonAbstractReason(SemaRef, Loc, D);
+}
+
 void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
   E = E->IgnoreParenImpCasts();
   if (E->containsErrors())
@@ -2681,6 +2742,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
       DiagnoseIsFinalReason(*this, E->getBeginLoc(), QT); // unsatisfied
     break;
   }
+  case UTT_IsAbstract:
+    DiagnoseNonAbstractReason(*this, E->getBeginLoc(), Args[0]);
+    break;
   default:
     break;
   }
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index 7c6c9ea4dde80..4fb55f85d565d 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -58,6 +58,13 @@ struct is_final {
 template <typename T>
 constexpr bool is_final_v = __is_final(T);
 
+template <typename T>
+struct is_abstract {
+    static constexpr bool value = __is_abstract(T);
+};
+template <typename T>
+constexpr bool is_abstract_v = __is_abstract(T);
+
 #endif
 
 #ifdef STD2
@@ -134,6 +141,15 @@ using is_final = __details_is_final<T>;
 template <typename T>
 constexpr bool is_final_v = __is_final(T);
 
+template <typename T>
+struct __details_is_abstract {
+    static constexpr bool value = __is_abstract(T);
+};
+template <typename T>
+using is_abstract = __details_is_abstract<T>;
+template <typename T>
+constexpr bool is_abstract_v = __is_abstract(T);
+
 #endif
 
 
@@ -203,6 +219,13 @@ using is_final = __details_is_final<T>;
 template <typename T>
 constexpr bool is_final_v = is_final<T>::value;
 
+template <typename T>
+struct __details_is_abstract : bool_constant<__is_abstract(T)> {};
+template <typename T>
+using is_abstract = __details_is_abstract<T>;
+template <typename T>
+constexpr bool is_abstract_v = is_abstract<T>::value;
+
 #endif
 
 }
@@ -299,6 +322,22 @@ static_assert(std::is_final_v<Arr>);
 // expected-note@-1 {{'int[3]' is not final}} \
 // expected-note@-1 {{because it is not a class or union type}}
 
+
+static_assert(!std::is_abstract<int>::value);
+
+static_assert(std::is_abstract<int&>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 
'std::{{.*}}is_abstract<int &>::value'}} \
+// 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(std::is_abstract_v<int&>);
+// expected-error@-1 {{static assertion failed due to requirement 
'std::is_abstract_v<int &>'}} \
+// 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}}
+
+
 namespace test_namespace {
     using namespace std;
     static_assert(is_trivially_relocatable<int&>::value);
@@ -376,6 +415,18 @@ namespace test_namespace {
     // expected-note@-1 {{'Fn' (aka 'void ()') is not final}} \
     // expected-note@-1 {{because it is a function type}} \
     // expected-note@-1 {{because it is not a class or union type}}
+
+    static_assert(is_abstract<int&>::value);
+    // expected-error-re@-1 {{static assertion failed due to requirement 
'{{.*}}is_abstract<int &>::value'}} \
+    // 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_abstract_v<int&>);
+    // expected-error@-1 {{static assertion failed due to requirement 
'is_abstract_v<int &>'}} \
+    // 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}}
 }
 
 
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 1619b0b22b85f..dff6ff59df275 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -872,3 +872,79 @@ namespace is_final_tests {
     // expected-note@-1 {{because it is not a class or union type}}
 
 }
+
+namespace is_abstract_tests {
+struct Abstract1 {
+  virtual void fn1() = 0;
+};
+
+struct Abstract2 {
+   virtual void fn2() = 0;
+};
+
+struct NonAbstract
+{
+   virtual void f() {}
+};
+
+// Multiple inheritance reports all abstract base classes that had their pure 
virtual functions overridden.
+struct Overrides : Abstract1, Abstract2, NonAbstract {
+  void fn1() override {}
+  void fn2() override {}
+};
+
+static_assert(__is_abstract(Overrides));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(is_abstract_tests::Overrides)'}} \
+// expected-note@-1 {{'Overrides' is not abstract}} \
+// expected-note@-1 {{because it overrides all pure virtual functions from 
base class 'Abstract1'}} \
+// expected-note@-1 {{because it overrides all pure virtual functions from 
base class 'Abstract2'}} \
+
+// Inheriting over two levels reports the last class only although the source 
of the pure virtual function
+// is the top-most base.
+struct Derived : Abstract1 {
+};
+
+struct Derived2 : Derived {
+   void fn1() override {}
+};
+
+static_assert(__is_abstract(Derived2));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(is_abstract_tests::Derived2)'}} \
+// expected-note@-1 {{'Derived2' is not abstract}} \
+// expected-note@-1 {{because it overrides all pure virtual functions from 
base class 'Derived'}} \
+
+
+using I = int;
+static_assert(__is_abstract(I));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(int)'}} \
+// expected-note@-1 {{'I' (aka 'int') is not abstract}} \
+// expected-note@-1 {{because it is not a struct or class type}}
+
+using Fty = void(); // function type
+static_assert(__is_abstract(Fty));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(void ())'}} \
+// expected-note@-1 {{'Fty' (aka 'void ()') is not abstract}} \
+// expected-note@-1 {{because it is a function type}} \
+// expected-note@-1 {{because it is not a struct or class type}}
+
+using Arr = int[3];
+static_assert(__is_abstract(Arr));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(int[3])'}} \
+// expected-note@-1 {{'Arr' (aka 'int[3]') is not abstract}} \
+// expected-note@-1 {{because it is not a struct or class type}}
+
+using Ref = int&;
+static_assert(__is_abstract(Ref));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(int &)'}} \
+// expected-note@-1 {{'Ref' (aka '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}}
+
+using Ptr = int*;
+static_assert(__is_abstract(Ptr));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_abstract(int *)'}} \
+// expected-note@-1 {{'Ptr' (aka 'int *') is not abstract}} \
+// expected-note@-1 {{because it is not a struct or class type}}
+
+
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to