[clang] [coroutine] Suppress unreachable-code warning on coroutine statements. (PR #77454)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 9 04:52:27 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Haojian Wu (hokein)
<details>
<summary>Changes</summary>
This fixes #<!-- -->69219.
Consider an example:
```
CoTask my_coroutine() {
std::abort();
co_return 1; // unreachable code warning.
}
```
Clang emits a CFG-based unreachable warning on the `co_return` statement (precisely the `1` subexpr). If we remove this statement, the program semantic is changed (my_coroutine is not a coroutine anymore).
The fix to suppress the warning for this particular case.
---
Full diff: https://github.com/llvm/llvm-project/pull/77454.diff
2 Files Affected:
- (modified) clang/lib/Analysis/ReachableCode.cpp (+41-1)
- (added) clang/test/SemaCXX/coroutine-unreachable-warning.cpp (+50)
``````````diff
diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp
index 1bf0d9aec8620e..d839d2f999609d 100644
--- a/clang/lib/Analysis/ReachableCode.cpp
+++ b/clang/lib/Analysis/ReachableCode.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
@@ -60,6 +61,45 @@ static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) {
return false;
}
+// Check if the block starts with a coroutine statement and see if the given
+// unreachable 'S' is the substmt of the coroutine statement.
+//
+// We suppress the unreachable warning for cases where an unreachable code is
+// a substmt of the coroutine statement, becase removing it will change the
+// function semantic if this is the only coroutine statement of the coroutine.
+static bool isInCoroutineStmt(const CFGBlock *Block, const Stmt* S) {
+ // The coroutine statement, co_return, co_await, or co_yield.
+ const Stmt* CoroStmt = nullptr;
+ // Find the first coroutine statement in the block.
+ for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E;
+ ++I)
+ if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
+ const Stmt *S = CS->getStmt();
+ if (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S)) {
+ CoroStmt = S ;
+ break;
+ }
+ }
+ if (!CoroStmt)
+ return false;
+
+ struct Checker : RecursiveASTVisitor<Checker> {
+ const Stmt *StmtToCheck;
+ bool CoroutineSubStmt = false;
+ Checker(const Stmt *S) : StmtToCheck(S) {}
+ bool VisitStmt(const Stmt *S) {
+ if (S == StmtToCheck)
+ CoroutineSubStmt = true;
+ return true;
+ }
+ // The 'S' stmt captured in the CFG can be implicit.
+ bool shouldVisitImplicitCode() const { return true; }
+ };
+ Checker checker(S);
+ checker.TraverseStmt(const_cast<Stmt *>(CoroStmt));
+ return checker.CoroutineSubStmt;
+}
+
static bool isBuiltinUnreachable(const Stmt *S) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(S))
if (const auto *FDecl = dyn_cast<FunctionDecl>(DRE->getDecl()))
@@ -623,7 +663,7 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B,
if (isa<BreakStmt>(S)) {
UK = reachable_code::UK_Break;
} else if (isTrivialDoWhile(B, S) || isBuiltinUnreachable(S) ||
- isBuiltinAssumeFalse(B, S, C)) {
+ isBuiltinAssumeFalse(B, S, C) || isInCoroutineStmt(B, S)) {
return;
}
else if (isDeadReturn(B, S)) {
diff --git a/clang/test/SemaCXX/coroutine-unreachable-warning.cpp b/clang/test/SemaCXX/coroutine-unreachable-warning.cpp
new file mode 100644
index 00000000000000..6ac5c34262b7e0
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-unreachable-warning.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -std=c++20 -fsyntax-only -verify -Wunreachable-code
+
+#include "Inputs/std-coroutine.h"
+
+extern void abort (void) __attribute__ ((__noreturn__));
+
+struct task {
+ struct promise_type {
+ std::suspend_always initial_suspend();
+ std::suspend_always final_suspend() noexcept;
+ void return_void();
+ std::suspend_always yield_value(int) { return {}; }
+ task get_return_object();
+ void unhandled_exception();
+ };
+};
+
+task test1() {
+ abort();
+ co_yield 1;
+}
+
+task test2() {
+ abort();
+ 1; // expected-warning {{code will never be executed}}
+ co_yield 1;
+}
+
+task test3() {
+ abort();
+ co_return;
+}
+
+task test4() {
+ abort();
+ 1; // expected-warning {{code will never be executed}}
+ co_return;
+}
+
+
+task test5() {
+ abort();
+ co_await std::suspend_never{};
+}
+
+task test6() {
+ abort();
+ 1; // expected-warning {{code will never be executed}}
+ co_await std::suspend_never{};
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/77454
More information about the cfe-commits
mailing list