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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits