Skip to content

Commit 6739046

Browse files
sebproellmahesh-attarde
authored andcommitted
[Clang] Add diagnostic for why std::is_abstract is false (llvm#156199)
Explain why a type is not abstract. Handles arrays, refs, unions, pointers, and functions. If the non-abstract type has abstract base classes, point out that their pure virtual methods must have been overridden. Adds onto llvm#141911
1 parent c5d733a commit 6739046

File tree

4 files changed

+221
-3
lines changed

4 files changed

+221
-3
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,8 @@ def note_unsatisfied_trait
17771777
"%Empty{empty}|"
17781778
"%StandardLayout{standard-layout}|"
17791779
"%Aggregate{aggregate}|"
1780-
"%Final{final}"
1780+
"%Final{final}|"
1781+
"%Abstract{abstract}"
17811782
"}1">;
17821783

17831784
def note_unsatisfied_trait_reason
@@ -1827,7 +1828,12 @@ def note_unsatisfied_trait_reason
18271828
"%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|"
18281829
"%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|"
18291830
"%NotClassOrUnion{is not a class or union type}|"
1830-
"%NotMarkedFinal{is not marked 'final'}"
1831+
"%NotMarkedFinal{is not marked 'final'}|"
1832+
"%PointerType{is a pointer type}|"
1833+
"%ArrayType{is an array type}|"
1834+
"%UnionType{is a union type}|"
1835+
"%NotStructOrClass{is not a struct or class type}|"
1836+
"%OverridesAllPureVirtual{overrides all pure virtual functions from base class %1}"
18311837
"}0">;
18321838

18331839
def warn_consteval_if_always_true : Warning<

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
20142014
.Case("is_aggregate", TypeTrait::UTT_IsAggregate)
20152015
.Case("is_constructible", TypeTrait::TT_IsConstructible)
20162016
.Case("is_final", TypeTrait::UTT_IsFinal)
2017+
.Case("is_abstract", TypeTrait::UTT_IsAbstract)
20172018
.Default(std::nullopt);
20182019
}
20192020

@@ -2774,6 +2775,75 @@ static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc,
27742775
DiagnoseNonAggregateReason(SemaRef, Loc, D);
27752776
}
27762777

2778+
static void DiagnoseNonAbstractReason(Sema &SemaRef, SourceLocation Loc,
2779+
const CXXRecordDecl *D) {
2780+
// If this type has any abstract base classes, their respective virtual
2781+
// functions must have been overridden.
2782+
for (const CXXBaseSpecifier &B : D->bases()) {
2783+
if (B.getType()->castAsCXXRecordDecl()->isAbstract()) {
2784+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2785+
<< diag::TraitNotSatisfiedReason::OverridesAllPureVirtual
2786+
<< B.getType() << B.getSourceRange();
2787+
}
2788+
}
2789+
}
2790+
2791+
static void DiagnoseNonAbstractReason(Sema &SemaRef, SourceLocation Loc,
2792+
QualType T) {
2793+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2794+
<< T << diag::TraitName::Abstract;
2795+
2796+
if (T->isReferenceType()) {
2797+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2798+
<< diag::TraitNotSatisfiedReason::Ref;
2799+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2800+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2801+
return;
2802+
}
2803+
2804+
if (T->isUnionType()) {
2805+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2806+
<< diag::TraitNotSatisfiedReason::UnionType;
2807+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2808+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2809+
return;
2810+
}
2811+
2812+
if (SemaRef.Context.getAsArrayType(T)) {
2813+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2814+
<< diag::TraitNotSatisfiedReason::ArrayType;
2815+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2816+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2817+
return;
2818+
}
2819+
2820+
if (T->isFunctionType()) {
2821+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2822+
<< diag::TraitNotSatisfiedReason::FunctionType;
2823+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2824+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2825+
return;
2826+
}
2827+
2828+
if (T->isPointerType()) {
2829+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2830+
<< diag::TraitNotSatisfiedReason::PointerType;
2831+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2832+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2833+
return;
2834+
}
2835+
2836+
if (!T->isStructureOrClassType()) {
2837+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2838+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2839+
return;
2840+
}
2841+
2842+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2843+
if (D->hasDefinition())
2844+
DiagnoseNonAbstractReason(SemaRef, Loc, D);
2845+
}
2846+
27772847
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
27782848
E = E->IgnoreParenImpCasts();
27792849
if (E->containsErrors())
@@ -2818,6 +2888,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
28182888
DiagnoseIsFinalReason(*this, E->getBeginLoc(), QT); // unsatisfied
28192889
break;
28202890
}
2891+
case UTT_IsAbstract:
2892+
DiagnoseNonAbstractReason(*this, E->getBeginLoc(), Args[0]);
2893+
break;
28212894
default:
28222895
break;
28232896
}

clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ struct is_final {
6666
template <typename T>
6767
constexpr bool is_final_v = __is_final(T);
6868

69+
template <typename T>
70+
struct is_abstract {
71+
static constexpr bool value = __is_abstract(T);
72+
};
73+
template <typename T>
74+
constexpr bool is_abstract_v = __is_abstract(T);
75+
6976
#endif
7077

7178
#ifdef STD2
@@ -151,6 +158,15 @@ using is_final = __details_is_final<T>;
151158
template <typename T>
152159
constexpr bool is_final_v = __is_final(T);
153160

161+
template <typename T>
162+
struct __details_is_abstract {
163+
static constexpr bool value = __is_abstract(T);
164+
};
165+
template <typename T>
166+
using is_abstract = __details_is_abstract<T>;
167+
template <typename T>
168+
constexpr bool is_abstract_v = __is_abstract(T);
169+
154170
#endif
155171

156172

@@ -229,6 +245,13 @@ using is_final = __details_is_final<T>;
229245
template <typename T>
230246
constexpr bool is_final_v = is_final<T>::value;
231247

248+
template <typename T>
249+
struct __details_is_abstract : bool_constant<__is_abstract(T)> {};
250+
template <typename T>
251+
using is_abstract = __details_is_abstract<T>;
252+
template <typename T>
253+
constexpr bool is_abstract_v = is_abstract<T>::value;
254+
232255
#endif
233256
}
234257

@@ -336,6 +359,22 @@ static_assert(std::is_aggregate_v<void>);
336359
// expected-note@-1 {{'void' is not aggregate}} \
337360
// expected-note@-1 {{because it is a cv void type}}
338361

362+
363+
static_assert(!std::is_abstract<int>::value);
364+
365+
static_assert(std::is_abstract<int&>::value);
366+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_abstract<int &>::value'}} \
367+
// expected-note@-1 {{'int &' is not abstract}} \
368+
// expected-note@-1 {{because it is a reference type}} \
369+
// expected-note@-1 {{because it is not a struct or class type}}
370+
371+
static_assert(std::is_abstract_v<int&>);
372+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_abstract_v<int &>'}} \
373+
// expected-note@-1 {{'int &' is not abstract}} \
374+
// expected-note@-1 {{because it is a reference type}} \
375+
// expected-note@-1 {{because it is not a struct or class type}}
376+
377+
339378
namespace test_namespace {
340379
using namespace std;
341380
static_assert(is_trivially_relocatable<int&>::value);
@@ -388,7 +427,7 @@ namespace test_namespace {
388427
static_assert(is_constructible_v<void>);
389428
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
390429
// expected-note@-1 {{because it is a cv void type}}
391-
430+
392431
static_assert(std::is_aggregate<void>::value);
393432
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_aggregate<void>::value'}} \
394433
// expected-note@-1 {{'void' is not aggregate}} \
@@ -422,6 +461,18 @@ namespace test_namespace {
422461
// expected-note@-1 {{'Fn' (aka 'void ()') is not final}} \
423462
// expected-note@-1 {{because it is a function type}} \
424463
// expected-note@-1 {{because it is not a class or union type}}
464+
465+
static_assert(is_abstract<int&>::value);
466+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_abstract<int &>::value'}} \
467+
// expected-note@-1 {{'int &' is not abstract}} \
468+
// expected-note@-1 {{because it is a reference type}} \
469+
// expected-note@-1 {{because it is not a struct or class type}}
470+
471+
static_assert(is_abstract_v<int&>);
472+
// expected-error@-1 {{static assertion failed due to requirement 'is_abstract_v<int &>'}} \
473+
// expected-note@-1 {{'int &' is not abstract}} \
474+
// expected-note@-1 {{because it is a reference type}} \
475+
// expected-note@-1 {{because it is not a struct or class type}}
425476
}
426477

427478

clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,3 +964,91 @@ namespace is_aggregate {
964964

965965
static_assert(__is_aggregate(S7[10]));
966966
}
967+
968+
namespace is_abstract_tests {
969+
struct Abstract1 {
970+
virtual void fn1() = 0;
971+
};
972+
973+
struct Abstract2 {
974+
virtual void fn2() = 0;
975+
};
976+
977+
struct NonAbstract
978+
{
979+
virtual void f() {}
980+
};
981+
982+
// Multiple inheritance reports all abstract base classes that had their pure virtual functions overridden.
983+
struct Overrides : Abstract1, Abstract2, NonAbstract {
984+
void fn1() override {}
985+
void fn2() override {}
986+
};
987+
988+
static_assert(__is_abstract(Overrides));
989+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(is_abstract_tests::Overrides)'}} \
990+
// expected-note@-1 {{'Overrides' is not abstract}} \
991+
// expected-note@-1 {{because it overrides all pure virtual functions from base class 'Abstract1'}} \
992+
// expected-note@-1 {{because it overrides all pure virtual functions from base class 'Abstract2'}} \
993+
994+
static_assert(__is_abstract(NonAbstract));
995+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(is_abstract_tests::NonAbstract)'}} \
996+
// expected-note@-1 {{'NonAbstract' is not abstract}}
997+
998+
// Inheriting over two levels reports the last class only although the source of the pure virtual function
999+
// is the top-most base.
1000+
struct Derived : Abstract1 {
1001+
};
1002+
1003+
struct Derived2 : Derived {
1004+
void fn1() override {}
1005+
};
1006+
1007+
static_assert(__is_abstract(Derived2));
1008+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(is_abstract_tests::Derived2)'}} \
1009+
// expected-note@-1 {{'Derived2' is not abstract}} \
1010+
// expected-note@-1 {{because it overrides all pure virtual functions from base class 'Derived'}} \
1011+
1012+
1013+
using I = int;
1014+
static_assert(__is_abstract(I));
1015+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int)'}} \
1016+
// expected-note@-1 {{'I' (aka 'int') is not abstract}} \
1017+
// expected-note@-1 {{because it is not a struct or class type}}
1018+
1019+
using Fty = void();
1020+
static_assert(__is_abstract(Fty));
1021+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(void ())'}} \
1022+
// expected-note@-1 {{'Fty' (aka 'void ()') is not abstract}} \
1023+
// expected-note@-1 {{because it is a function type}} \
1024+
// expected-note@-1 {{because it is not a struct or class type}}
1025+
1026+
using Arr = int[3];
1027+
static_assert(__is_abstract(Arr));
1028+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int[3])'}} \
1029+
// expected-note@-1 {{'Arr' (aka 'int[3]') is not abstract}} \
1030+
// expected-note@-1 {{because it is an array type}} \
1031+
// expected-note@-1 {{because it is not a struct or class type}}
1032+
1033+
using Ref = int&;
1034+
static_assert(__is_abstract(Ref));
1035+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int &)'}} \
1036+
// expected-note@-1 {{'Ref' (aka 'int &') is not abstract}} \
1037+
// expected-note@-1 {{because it is a reference type}} \
1038+
// expected-note@-1 {{because it is not a struct or class type}}
1039+
1040+
using Ptr = int*;
1041+
static_assert(__is_abstract(Ptr));
1042+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int *)'}} \
1043+
// expected-note@-1 {{'Ptr' (aka 'int *') is not abstract}} \
1044+
// expected-note@-1 {{because it is a pointer type}} \
1045+
// expected-note@-1 {{because it is not a struct or class type}}
1046+
1047+
union U { int x; float y;};
1048+
static_assert(__is_abstract(U));
1049+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(is_abstract_tests::U)'}} \
1050+
// expected-note@-1 {{'U' is not abstract}} \
1051+
// expected-note@-1 {{because it is a union type}} \
1052+
// expected-note@-1 {{because it is not a struct or class type}}
1053+
1054+
}

0 commit comments

Comments
 (0)