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