@@ -61,45 +61,6 @@ static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) {
61
61
return false ;
62
62
}
63
63
64
- // Check if the block starts with a coroutine statement and see if the given
65
- // unreachable 'S' is the substmt of the coroutine statement.
66
- //
67
- // We suppress the unreachable warning for cases where an unreachable code is
68
- // a substmt of the coroutine statement, becase removing it will change the
69
- // function semantic if this is the only coroutine statement of the coroutine.
70
- static bool isInCoroutineStmt (const CFGBlock *Block, const Stmt* S) {
71
- // The coroutine statement, co_return, co_await, or co_yield.
72
- const Stmt* CoroStmt = nullptr ;
73
- // Find the first coroutine statement in the block.
74
- for (CFGBlock::const_iterator I = Block->begin (), E = Block->end (); I != E;
75
- ++I)
76
- if (std::optional<CFGStmt> CS = I->getAs <CFGStmt>()) {
77
- const Stmt *S = CS->getStmt ();
78
- if (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S)) {
79
- CoroStmt = S ;
80
- break ;
81
- }
82
- }
83
- if (!CoroStmt)
84
- return false ;
85
-
86
- struct Checker : RecursiveASTVisitor<Checker> {
87
- const Stmt *StmtToCheck;
88
- bool CoroutineSubStmt = false ;
89
- Checker (const Stmt *S) : StmtToCheck(S) {}
90
- bool VisitStmt (const Stmt *S) {
91
- if (S == StmtToCheck)
92
- CoroutineSubStmt = true ;
93
- return true ;
94
- }
95
- // The 'S' stmt captured in the CFG can be implicit.
96
- bool shouldVisitImplicitCode () const { return true ; }
97
- };
98
- Checker checker (S);
99
- checker.TraverseStmt (const_cast <Stmt *>(CoroStmt));
100
- return checker.CoroutineSubStmt ;
101
- }
102
-
103
64
static bool isBuiltinUnreachable (const Stmt *S) {
104
65
if (const auto *DRE = dyn_cast<DeclRefExpr>(S))
105
66
if (const auto *FDecl = dyn_cast<FunctionDecl>(DRE->getDecl ()))
@@ -493,26 +454,68 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) {
493
454
return isDeadRoot;
494
455
}
495
456
496
- static bool isValidDeadStmt (const Stmt *S) {
457
+ // Check if the given `DeadStmt` is a coroutine statement and is a substmt of
458
+ // the coroutine statement.
459
+ static bool isInCoroutineStmt (const Stmt* DeadStmt, const CFGBlock *Block) {
460
+ // The coroutine statement, co_return, co_await, or co_yield.
461
+ const Stmt* CoroStmt = nullptr ;
462
+ // Find the first coroutine statement after the DeadStmt in the block.
463
+ bool AfterDeadStmt = false ;
464
+ for (CFGBlock::const_iterator I = Block->begin (), E = Block->end (); I != E;
465
+ ++I)
466
+ if (std::optional<CFGStmt> CS = I->getAs <CFGStmt>()) {
467
+ const Stmt *S = CS->getStmt ();
468
+ if (S == DeadStmt)
469
+ AfterDeadStmt = true ;
470
+ if (AfterDeadStmt &&
471
+ (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S))) {
472
+ CoroStmt = S;
473
+ break ;
474
+ }
475
+ }
476
+ if (!CoroStmt)
477
+ return false ;
478
+
479
+ struct Checker : RecursiveASTVisitor<Checker> {
480
+ const Stmt *StmtToCheck;
481
+ bool CoroutineSubStmt = false ;
482
+ Checker (const Stmt *S) : StmtToCheck(S) {}
483
+ bool VisitStmt (const Stmt *S) {
484
+ if (S == StmtToCheck)
485
+ CoroutineSubStmt = true ;
486
+ return true ;
487
+ }
488
+ // Statements captured in the CFG can be implicit.
489
+ bool shouldVisitImplicitCode () const { return true ; }
490
+ };
491
+ Checker checker (DeadStmt);
492
+ checker.TraverseStmt (const_cast <Stmt *>(CoroStmt));
493
+ return checker.CoroutineSubStmt ;
494
+ }
495
+
496
+ static bool isValidDeadStmt (const Stmt *S, const clang::CFGBlock *Block) {
497
497
if (S->getBeginLoc ().isInvalid ())
498
498
return false ;
499
499
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S))
500
500
return BO->getOpcode () != BO_Comma;
501
- return true ;
501
+ // Coroutine statements are never considered dead statements, because removing
502
+ // them may change the function semantic if it is the only coroutine statement
503
+ // of the coroutine.
504
+ return !isInCoroutineStmt (S, Block);
502
505
}
503
506
504
507
const Stmt *DeadCodeScan::findDeadCode (const clang::CFGBlock *Block) {
505
508
for (CFGBlock::const_iterator I = Block->begin (), E = Block->end (); I!=E; ++I)
506
509
if (std::optional<CFGStmt> CS = I->getAs <CFGStmt>()) {
507
510
const Stmt *S = CS->getStmt ();
508
- if (isValidDeadStmt (S))
511
+ if (isValidDeadStmt (S, Block ))
509
512
return S;
510
513
}
511
514
512
515
CFGTerminator T = Block->getTerminator ();
513
516
if (T.isStmtBranch ()) {
514
517
const Stmt *S = T.getStmt ();
515
- if (S && isValidDeadStmt (S))
518
+ if (S && isValidDeadStmt (S, Block ))
516
519
return S;
517
520
}
518
521
@@ -663,7 +666,7 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B,
663
666
if (isa<BreakStmt>(S)) {
664
667
UK = reachable_code::UK_Break;
665
668
} else if (isTrivialDoWhile (B, S) || isBuiltinUnreachable (S) ||
666
- isBuiltinAssumeFalse (B, S, C) || isInCoroutineStmt (B, S) ) {
669
+ isBuiltinAssumeFalse (B, S, C)) {
667
670
return ;
668
671
}
669
672
else if (isDeadReturn (B, S)) {
0 commit comments