[clang] [LifetimeSafety] Store cleanup statements for temporaries (PR #200568)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 00:35:15 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/200568
>From 542646f0370fe96728088103dadb0b7ef690b8a9 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sat, 30 May 2026 14:56:48 +0300
Subject: [PATCH 1/2] [LifetimeSafety] Store cleanup expression for temporaries
---
clang/include/clang/Analysis/CFG.h | 14 ++++++++---
clang/lib/Analysis/CFG.cpp | 25 +++++++++++--------
.../LifetimeSafety/FactsGenerator.cpp | 7 +++---
clang/test/Sema/warn-lifetime-safety.cpp | 19 ++++++++++++--
4 files changed, 45 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h
index 72b7ac013ccc2..bd819247950db 100644
--- a/clang/include/clang/Analysis/CFG.h
+++ b/clang/include/clang/Analysis/CFG.h
@@ -318,8 +318,14 @@ class CFGFullExprCleanup : public CFGElement {
public:
using MTEVecTy = BumpVector<const MaterializeTemporaryExpr *>;
- explicit CFGFullExprCleanup(const MTEVecTy *MTEs)
- : CFGElement(FullExprCleanup, MTEs, nullptr) {}
+ explicit CFGFullExprCleanup(const MTEVecTy *MTEs, const Stmt *CleanupStmt)
+ : CFGElement(FullExprCleanup, MTEs, CleanupStmt) {}
+
+ const Stmt *getCleanupStmt() const {
+ return static_cast<const Stmt *>(Data2.getPointer());
+ }
+
+ SourceLocation getCleanupLoc() const { return getCleanupStmt()->getEndLoc(); }
ArrayRef<const MaterializeTemporaryExpr *> getExpiringMTEs() const {
const MTEVecTy *ExpiringMTEs =
@@ -1211,8 +1217,8 @@ class CFGBlock {
}
void appendFullExprCleanup(BumpVector<const MaterializeTemporaryExpr *> *BV,
- BumpVectorContext &C) {
- Elements.push_back(CFGFullExprCleanup(BV), C);
+ const Stmt *CleanupStmt, BumpVectorContext &C) {
+ Elements.push_back(CFGFullExprCleanup(BV, CleanupStmt), C);
}
void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) {
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 1b4d10b72e249..c1ccdd3cf9c35 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -813,7 +813,8 @@ class CFGBuilder {
void addScopeChangesHandling(LocalScope::const_iterator SrcPos,
LocalScope::const_iterator DstPos,
Stmt *S);
- void addFullExprCleanupMarker(TempDtorContext &Context);
+ void addFullExprCleanupMarker(TempDtorContext &Context,
+ const Stmt *CleanupStmt);
CFGBlock *createScopeChangesHandlingBlock(LocalScope::const_iterator SrcPos,
CFGBlock *SrcBlk,
LocalScope::const_iterator DstPost,
@@ -1841,10 +1842,10 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
(BuildOpts.AddTemporaryDtors || BuildOpts.AddLifetime)) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
- VisitForTemporaries(cast<ExprWithCleanups>(ActualInit)->getSubExpr(),
- /*ExternallyDestructed=*/false, Context);
+ Expr *FullExpr = cast<ExprWithCleanups>(ActualInit)->getSubExpr();
+ VisitForTemporaries(FullExpr, /*ExternallyDestructed=*/false, Context);
- addFullExprCleanupMarker(Context);
+ addFullExprCleanupMarker(Context, FullExpr);
}
}
@@ -2096,7 +2097,8 @@ void CFGBuilder::addScopeChangesHandling(LocalScope::const_iterator SrcPos,
addAutomaticObjHandling(SrcPos, BasePos, S);
}
-void CFGBuilder::addFullExprCleanupMarker(TempDtorContext &Context) {
+void CFGBuilder::addFullExprCleanupMarker(TempDtorContext &Context,
+ const Stmt *CleanupStmt) {
CFGFullExprCleanup::MTEVecTy *ExpiringMTEs = nullptr;
BumpVectorContext &BVC = cfg->getBumpVectorContext();
@@ -2107,7 +2109,7 @@ void CFGBuilder::addFullExprCleanupMarker(TempDtorContext &Context) {
CFGFullExprCleanup::MTEVecTy(BVC, NumCollected);
for (const MaterializeTemporaryExpr *MTE : Context.CollectedMTEs)
ExpiringMTEs->push_back(MTE, BVC);
- Block->appendFullExprCleanup(ExpiringMTEs, BVC);
+ Block->appendFullExprCleanup(ExpiringMTEs, CleanupStmt, BVC);
}
}
@@ -3173,10 +3175,10 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
(BuildOpts.AddTemporaryDtors || BuildOpts.AddLifetime)) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
- VisitForTemporaries(cast<ExprWithCleanups>(Init)->getSubExpr(),
- /*ExternallyDestructed=*/true, Context);
+ Expr *FullExpr = cast<ExprWithCleanups>(Init)->getSubExpr();
+ VisitForTemporaries(FullExpr, /*ExternallyDestructed=*/true, Context);
- addFullExprCleanupMarker(Context);
+ addFullExprCleanupMarker(Context, FullExpr);
}
}
@@ -4990,9 +4992,10 @@ CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
// If adding implicit destructors visit the full expression for adding
// destructors of temporaries.
TempDtorContext Context;
- VisitForTemporaries(E->getSubExpr(), ExternallyDestructed, Context);
+ Expr *FullExpr = E->getSubExpr();
+ VisitForTemporaries(FullExpr, ExternallyDestructed, Context);
- addFullExprCleanupMarker(Context);
+ addFullExprCleanupMarker(Context, FullExpr);
// Full expression has to be added as CFGStmt so it will be sequenced
// before destructors of it's temporaries.
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index b34a04f1f3bda..5681d0ce35e90 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -725,9 +725,10 @@ void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
void FactsGenerator::handleFullExprCleanup(
const CFGFullExprCleanup &FullExprCleanup) {
- for (const auto *MTE : FullExprCleanup.getExpiringMTEs())
- CurrentBlockFacts.push_back(
- FactMgr.createFact<ExpireFact>(AccessPath(MTE), MTE->getEndLoc()));
+ for (const auto *MTE : FullExprCleanup.getExpiringMTEs()) {
+ CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
+ AccessPath(MTE), FullExprCleanup.getCleanupLoc()));
+ }
}
void FactsGenerator::handleExitBlock() {
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 06097d4600af5..461287b4ecb9a 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1287,6 +1287,21 @@ void use_trivial_temporary_after_destruction() {
use(a); // expected-note {{later used here}}
}
+namespace FullExprCleanupLoc {
+void var_initializer() {
+ View v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}}
+ .getView(); // expected-note {{destroyed here}}
+ v.use(); // expected-note {{later used here}}
+}
+
+void expr_statement() {
+ View v;
+ v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}}
+ .getView(); // expected-note {{destroyed here}}
+ v.use(); // expected-note {{later used here}}
+}
+} // namespace FullExprCleanupLoc
+
namespace GH162834 {
// https://github.com/llvm/llvm-project/issues/162834
template <class T>
@@ -2299,8 +2314,8 @@ struct S {
void indexing_with_static_operator() {
S()(1, 2);
S& x = S()("1",
- 2, // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
- 3); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
+ 2, // expected-warning {{local temporary object does not live long enough}}
+ 3); // expected-warning {{local temporary object does not live long enough}} expected-note 2 {{destroyed here}}
(void)x; // expected-note 2 {{later used here}}
>From 11d9ced57bc821f376bc5779b69b0ddc4007fad1 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Mon, 1 Jun 2026 10:35:00 +0300
Subject: [PATCH 2/2] remove braces
---
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 5681d0ce35e90..02be730443d20 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -725,10 +725,9 @@ void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
void FactsGenerator::handleFullExprCleanup(
const CFGFullExprCleanup &FullExprCleanup) {
- for (const auto *MTE : FullExprCleanup.getExpiringMTEs()) {
+ for (const auto *MTE : FullExprCleanup.getExpiringMTEs())
CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
AccessPath(MTE), FullExprCleanup.getCleanupLoc()));
- }
}
void FactsGenerator::handleExitBlock() {
More information about the cfe-commits
mailing list