Skip to content

Commit ce6fb88

Browse files
committed
[Clang] Add diagnostic for why std::is_abstract is false
1 parent 07d0225 commit ce6fb88

File tree

4 files changed

+198
-3
lines changed

4 files changed

+198
-3
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,8 @@ def note_unsatisfied_trait
17741774
"%Empty{empty}|"
17751775
"%StandardLayout{standard-layout}|"
17761776
"%Aggregate{aggregate}|"
1777-
"%Final{final}"
1777+
"%Final{final}|"
1778+
"%Abstract{abstract}"
17781779
"}1">;
17791780

17801781
def note_unsatisfied_trait_reason
@@ -1824,7 +1825,10 @@ def note_unsatisfied_trait_reason
18241825
"%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|"
18251826
"%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|"
18261827
"%NotClassOrUnion{is not a class or union type}|"
1827-
"%NotMarkedFinal{is not marked 'final'}"
1828+
"%NotMarkedFinal{is not marked 'final'}|"
1829+
"%UnionType{is a union type}|"
1830+
"%NotStructOrClass{is not a struct or class type}|"
1831+
"%OverridesAllPureVirtual{overrides all pure virtual functions from base class %1}"
18281832
"}0">;
18291833

18301834
def warn_consteval_if_always_true : Warning<

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 64 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,66 @@ 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+
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2784+
if (B.getType()->getAsCXXRecordDecl()->isAbstract()) {
2785+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2786+
<< diag::TraitNotSatisfiedReason::OverridesAllPureVirtual
2787+
<< B.getType() << B.getSourceRange();
2788+
}
2789+
}
2790+
}
2791+
2792+
static void DiagnoseNonAbstractReason(Sema &SemaRef, SourceLocation Loc,
2793+
QualType T) {
2794+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2795+
<< T << diag::TraitName::Abstract;
2796+
2797+
if (T->isReferenceType()) {
2798+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2799+
<< diag::TraitNotSatisfiedReason::Ref;
2800+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2801+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2802+
return;
2803+
}
2804+
2805+
if (T->isUnionType()) {
2806+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2807+
<< diag::TraitNotSatisfiedReason::UnionType;
2808+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2809+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2810+
return;
2811+
}
2812+
2813+
if (SemaRef.Context.getAsArrayType(T)) {
2814+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2815+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2816+
return;
2817+
}
2818+
2819+
if (T->isFunctionType()) {
2820+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2821+
<< diag::TraitNotSatisfiedReason::FunctionType;
2822+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2823+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2824+
return;
2825+
}
2826+
2827+
if (!T->isStructureOrClassType()) {
2828+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2829+
<< diag::TraitNotSatisfiedReason::NotStructOrClass;
2830+
return;
2831+
}
2832+
2833+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2834+
if (D->hasDefinition())
2835+
DiagnoseNonAbstractReason(SemaRef, Loc, D);
2836+
}
2837+
27772838
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
27782839
E = E->IgnoreParenImpCasts();
27792840
if (E->containsErrors())
@@ -2818,6 +2879,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
28182879
DiagnoseIsFinalReason(*this, E->getBeginLoc(), QT); // unsatisfied
28192880
break;
28202881
}
2882+
case UTT_IsAbstract:
2883+
DiagnoseNonAbstractReason(*this, E->getBeginLoc(), Args[0]);
2884+
break;
28212885
default:
28222886
break;
28232887
}

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: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,3 +964,79 @@ 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+
// Inheriting over two levels reports the last class only although the source of the pure virtual function
995+
// is the top-most base.
996+
struct Derived : Abstract1 {
997+
};
998+
999+
struct Derived2 : Derived {
1000+
void fn1() override {}
1001+
};
1002+
1003+
static_assert(__is_abstract(Derived2));
1004+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(is_abstract_tests::Derived2)'}} \
1005+
// expected-note@-1 {{'Derived2' is not abstract}} \
1006+
// expected-note@-1 {{because it overrides all pure virtual functions from base class 'Derived'}} \
1007+
1008+
1009+
using I = int;
1010+
static_assert(__is_abstract(I));
1011+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int)'}} \
1012+
// expected-note@-1 {{'I' (aka 'int') is not abstract}} \
1013+
// expected-note@-1 {{because it is not a struct or class type}}
1014+
1015+
using Fty = void(); // function type
1016+
static_assert(__is_abstract(Fty));
1017+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(void ())'}} \
1018+
// expected-note@-1 {{'Fty' (aka 'void ()') is not abstract}} \
1019+
// expected-note@-1 {{because it is a function type}} \
1020+
// expected-note@-1 {{because it is not a struct or class type}}
1021+
1022+
using Arr = int[3];
1023+
static_assert(__is_abstract(Arr));
1024+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int[3])'}} \
1025+
// expected-note@-1 {{'Arr' (aka 'int[3]') is not abstract}} \
1026+
// expected-note@-1 {{because it is not a struct or class type}}
1027+
1028+
using Ref = int&;
1029+
static_assert(__is_abstract(Ref));
1030+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int &)'}} \
1031+
// expected-note@-1 {{'Ref' (aka 'int &') is not abstract}} \
1032+
// expected-note@-1 {{because it is a reference type}} \
1033+
// expected-note@-1 {{because it is not a struct or class type}}
1034+
1035+
using Ptr = int*;
1036+
static_assert(__is_abstract(Ptr));
1037+
// expected-error@-1 {{static assertion failed due to requirement '__is_abstract(int *)'}} \
1038+
// expected-note@-1 {{'Ptr' (aka 'int *') is not abstract}} \
1039+
// expected-note@-1 {{because it is not a struct or class type}}
1040+
1041+
1042+
}

0 commit comments

Comments
 (0)