-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Add diagnostic for why std::is_abstract is false #156199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2014,6 +2014,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) { | |
| .Case("is_aggregate", TypeTrait::UTT_IsAggregate) | ||
| .Case("is_constructible", TypeTrait::TT_IsConstructible) | ||
| .Case("is_final", TypeTrait::UTT_IsFinal) | ||
| .Case("is_abstract", TypeTrait::UTT_IsAbstract) | ||
| .Default(std::nullopt); | ||
| } | ||
|
|
||
|
|
@@ -2774,6 +2775,66 @@ static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc, | |
| DiagnoseNonAggregateReason(SemaRef, Loc, 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()) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This case does not look covered in the tests. Always make sure you cover all the diagnostics and various branches. |
||
| 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()) | ||
|
|
@@ -2818,6 +2879,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; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -964,3 +964,79 @@ namespace is_aggregate { | |
|
|
||
| static_assert(__is_aggregate(S7[10])); | ||
| } | ||
|
|
||
| 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 | ||
sebproell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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}} \ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True. I was looking at
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can deal w/ this be done as a follow-up |
||
| // 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}} | ||
|
|
||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.