-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. #115342
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
[Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. #115342
Changes from 1 commit
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 |
|---|---|---|
|
|
@@ -972,6 +972,7 @@ class Analyzer { | |
| CallableInfo &CurrentCaller; | ||
| ViolationSite VSite; | ||
| const Expr *TrailingRequiresClause = nullptr; | ||
| const Expr *NoexceptExpr = nullptr; | ||
|
|
||
| FunctionBodyASTVisitor(Analyzer &Outer, | ||
| PendingFunctionAnalysis &CurrentFunction, | ||
|
|
@@ -986,9 +987,22 @@ class Analyzer { | |
| if (auto *Dtor = dyn_cast<CXXDestructorDecl>(CurrentCaller.CDecl)) | ||
| followDestructor(dyn_cast<CXXRecordDecl>(Dtor->getParent()), Dtor); | ||
|
|
||
| if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl)) | ||
| if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl)) { | ||
| TrailingRequiresClause = FD->getTrailingRequiresClause(); | ||
|
|
||
| // Note that FD->getType->getAs<FunctionProtoType>() can yield a | ||
| // noexcept Expr which has been boiled down to a constant expression. | ||
| // Going through the TypeSourceInfo obtains the actual expression which | ||
| // will be traversed as part of the function -- unless we capture it | ||
| // here and have TraverseStmt skip it. | ||
| if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) { | ||
| FunctionProtoTypeLoc TL = | ||
| TSI->getTypeLoc().getAs<FunctionProtoTypeLoc>(); | ||
| if (const FunctionProtoType *FPT = TL.getTypePtr()) | ||
| NoexceptExpr = FPT->getNoexceptExpr(); | ||
| } | ||
| } | ||
|
|
||
| // Do an AST traversal of the function/block body | ||
| TraverseDecl(const_cast<Decl *>(CurrentCaller.CDecl)); | ||
|
Member
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. At this point, I wonder if it wouldn’t be easier to just reimplement function traversal and not call
Member
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. The current approach should work too, though. So if you want to explore that approach separately (or I can also take a look at that since I’ve been refactoring AST visitors lately), then that’s also fine imo
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. That's a good thought. Looking at
I'm not excited to tear this apart at the moment but maybe the next bug that comes up in this area can drive an improvement. |
||
| } | ||
|
|
@@ -1269,7 +1283,8 @@ class Analyzer { | |
| // We skip the traversal of lambdas (beyond their captures, see | ||
| // TraverseLambdaExpr below), so just caching this from our constructor | ||
| // should suffice. | ||
| if (Statement != TrailingRequiresClause) | ||
| // The exact same is true for a conditional `noexcept()` clause. | ||
| if (Statement != TrailingRequiresClause && Statement != NoexceptExpr) | ||
| return Base::TraverseStmt(Statement); | ||
| return true; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -388,7 +388,7 @@ void nb26() [[clang::nonblocking]] { | |
| abort_wrapper(); // no diagnostic | ||
| } | ||
|
|
||
| // --- Make sure we don't traverse a requires clause. --- | ||
| // --- Make sure we don't traverse requires and noexcept clauses. --- | ||
|
|
||
| // Apparently some requires clauses are able to be collapsed into a constant before the nonblocking | ||
| // analysis sees any function calls. This example (extracted from a real-world case where | ||
|
|
@@ -420,19 +420,27 @@ class expected { | |
| constexpr expected() | ||
| {} | ||
|
|
||
| // This is a deliberate corruption of the real implementation for simplicity. | ||
| constexpr expected(const expected&) | ||
| noexcept(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>) | ||
|
||
| requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>) | ||
| = default; | ||
| }; | ||
|
|
||
| void test() [[clang::nonblocking]] | ||
| { | ||
| expected<int, int> a; | ||
| auto b = a; | ||
| auto b = a; // Copy constructor. | ||
| } | ||
|
|
||
| } // namespace ExpectedTest | ||
|
|
||
| // Make sure that simple type traits don't cause violations. | ||
|
|
||
| void nb27() [[clang::nonblocking]] { | ||
| bool x = __is_constructible(int, const int&); | ||
| } | ||
|
|
||
| // --- nonblocking implies noexcept --- | ||
| #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept" | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.