diff options
author | yronglin <yronglin777@gmail.com> | 2025-02-01 16:58:05 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-01 16:58:05 +0800 |
commit | 44aa618ef67d302f5ab77cc591fb3434fe967a2e (patch) | |
tree | e7e8c4e46bfa1057f18786047748292b832072e0 /clang/lib/Analysis | |
parent | 69905810c483811abff5f9971799bc8a32eb4514 (diff) | |
download | llvm-44aa618ef67d302f5ab77cc591fb3434fe967a2e.zip llvm-44aa618ef67d302f5ab77cc591fb3434fe967a2e.tar.gz llvm-44aa618ef67d302f5ab77cc591fb3434fe967a2e.tar.bz2 |
[Analyzer][CFG] Correctly handle rebuilt default arg and default init expression (#117437)
Clang currently support extending lifetime of object bound to reference
members of aggregates, that are created from default member initializer.
This PR address this change and updaye CFG and ExprEngine.
This PR reapply https://github.com/llvm/llvm-project/pull/91879.
Fixes https://github.com/llvm/llvm-project/issues/93725.
---------
Signed-off-by: yronglin <yronglin777@gmail.com>
Diffstat (limited to 'clang/lib/Analysis')
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 50 | ||||
-rw-r--r-- | clang/lib/Analysis/ReachableCode.cpp | 37 |
2 files changed, 60 insertions, 27 deletions
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 304bbb2..6bba0e3 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -556,6 +556,10 @@ public: private: // Visitors to walk an AST and construct the CFG. + CFGBlock *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Default, + AddStmtChoice asc); + CFGBlock *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Default, + AddStmtChoice asc); CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc); CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc); CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc); @@ -2261,16 +2265,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, asc, ExternallyDestructed); case Stmt::CXXDefaultArgExprClass: + return VisitCXXDefaultArgExpr(cast<CXXDefaultArgExpr>(S), asc); + case Stmt::CXXDefaultInitExprClass: - // FIXME: The expression inside a CXXDefaultArgExpr is owned by the - // called function's declaration, not by the caller. If we simply add - // this expression to the CFG, we could end up with the same Expr - // appearing multiple times (PR13385). - // - // It's likewise possible for multiple CXXDefaultInitExprs for the same - // expression to be used in the same function (through aggregate - // initialization). - return VisitStmt(S, asc); + return VisitCXXDefaultInitExpr(cast<CXXDefaultInitExpr>(S), asc); case Stmt::CXXBindTemporaryExprClass: return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc); @@ -2440,6 +2438,40 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) { return B; } +CFGBlock *CFGBuilder::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Arg, + AddStmtChoice asc) { + if (Arg->hasRewrittenInit()) { + if (asc.alwaysAdd(*this, Arg)) { + autoCreateBlock(); + appendStmt(Block, Arg); + } + return VisitStmt(Arg->getExpr(), asc); + } + + // We can't add the default argument if it's not rewritten because the + // expression inside a CXXDefaultArgExpr is owned by the called function's + // declaration, not by the caller, we could end up with the same expression + // appearing multiple times. + return VisitStmt(Arg, asc); +} + +CFGBlock *CFGBuilder::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Init, + AddStmtChoice asc) { + if (Init->hasRewrittenInit()) { + if (asc.alwaysAdd(*this, Init)) { + autoCreateBlock(); + appendStmt(Block, Init); + } + return VisitStmt(Init->getExpr(), asc); + } + + // We can't add the default initializer if it's not rewritten because multiple + // CXXDefaultInitExprs for the same sub-expression to be used in the same + // function (through aggregate initialization). we could end up with the same + // expression appearing multiple times. + return VisitStmt(Init, asc); +} + CFGBlock *CFGBuilder::VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc) { if (asc.alwaysAdd(*this, ILE)) { autoCreateBlock(); diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index dd81c8e..3b1f716 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -454,11 +454,12 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { return isDeadRoot; } -// Check if the given `DeadStmt` is a coroutine statement and is a substmt of -// the coroutine statement. `Block` is the CFGBlock containing the `DeadStmt`. -static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) { +// Check if the given `DeadStmt` is one of target statements or is a sub-stmt of +// them. `Block` is the CFGBlock containing the `DeadStmt`. +template <class... Ts> +static bool isDeadStmtInOneOf(const Stmt *DeadStmt, const CFGBlock *Block) { // The coroutine statement, co_return, co_await, or co_yield. - const Stmt *CoroStmt = nullptr; + const Stmt *TargetStmt = nullptr; // Find the first coroutine statement after the DeadStmt in the block. bool AfterDeadStmt = false; for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E; @@ -467,32 +468,27 @@ static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) { const Stmt *S = CS->getStmt(); if (S == DeadStmt) AfterDeadStmt = true; - if (AfterDeadStmt && - // For simplicity, we only check simple coroutine statements. - (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S))) { - CoroStmt = S; + if (AfterDeadStmt && llvm::isa<Ts...>(S)) { + TargetStmt = S; break; } } - if (!CoroStmt) + if (!TargetStmt) return false; struct Checker : DynamicRecursiveASTVisitor { const Stmt *DeadStmt; - bool CoroutineSubStmt = false; - Checker(const Stmt *S) : DeadStmt(S) { - // Statements captured in the CFG can be implicit. - ShouldVisitImplicitCode = true; - } + bool IsSubStmtOfTargetStmt = false; + Checker(const Stmt *S) : DeadStmt(S) { ShouldVisitImplicitCode = true; } bool VisitStmt(Stmt *S) override { if (S == DeadStmt) - CoroutineSubStmt = true; + IsSubStmtOfTargetStmt = true; return true; } }; Checker checker(DeadStmt); - checker.TraverseStmt(const_cast<Stmt *>(CoroStmt)); - return checker.CoroutineSubStmt; + checker.TraverseStmt(const_cast<Stmt *>(TargetStmt)); + return checker.IsSubStmtOfTargetStmt; } static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) { @@ -503,7 +499,12 @@ static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) { // Coroutine statements are never considered dead statements, because removing // them may change the function semantic if it is the only coroutine statement // of the coroutine. - return !isInCoroutineStmt(S, Block); + // + // If the dead stmt is a sub-stmt of CXXDefaultInitExpr and CXXDefaultArgExpr, + // we would rather expect to find CXXDefaultInitExpr and CXXDefaultArgExpr as + // a valid dead stmt. + return !isDeadStmtInOneOf<CoreturnStmt, CoroutineSuspendExpr, + CXXDefaultArgExpr, CXXDefaultInitExpr>(S, Block); } const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { |