[llvm-branch-commits] [clang] [LifetimeSafety] Handle temporaries of non-trivial view types (PR #177878)
Utkarsh Saxena via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jan 27 06:51:40 PST 2026
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/177878
>From caa67f558025ef41b97eb65738c8794d4505f174 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Sat, 24 Jan 2026 20:02:17 +0000
Subject: [PATCH] Handle non-trivial view types of temporaries
---
.../Analyses/LifetimeSafety/FactsGenerator.h | 1 +
.../LifetimeSafety/FactsGenerator.cpp | 17 ++--
clang/test/Sema/warn-lifetime-safety.cpp | 97 +++++--------------
3 files changed, 33 insertions(+), 82 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index e4487b0d1dbc7..8b45337bee218 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -48,6 +48,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE);
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE);
void VisitInitListExpr(const InitListExpr *ILE);
+ void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE);
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
private:
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 98fadaa11137f..dea868fd22ddd 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -418,12 +418,14 @@ void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {
killAndFlowOrigin(*ILE, *ILE->getInit(0));
}
+void FactsGenerator::VisitCXXBindTemporaryExpr(
+ const CXXBindTemporaryExpr *BTE) {
+ killAndFlowOrigin(*BTE, *BTE->getSubExpr());
+}
+
void FactsGenerator::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *MTE) {
assert(MTE->isGLValue());
- // We defer from handling lifetime extended materializations.
- if (MTE->getStorageDuration() != SD_FullExpression)
- return;
OriginList *MTEList = getOriginsList(*MTE);
if (!MTEList)
return;
@@ -431,15 +433,16 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
assert((!SubExprList ||
MTEList->getLength() == (SubExprList->getLength() + 1)) &&
"MTE top level origin should contain a loan to the MTE itself");
- MTEList = getRValueOrigins(MTE, MTEList);
+
+ OriginList *RValMTEList = getRValueOrigins(MTE, MTEList);
+ flow(RValMTEList, SubExprList, /*Kill=*/true);
+ OriginID OuterMTEID = MTEList->getOuterOriginID();
if (getChildBinding(MTE)) {
// Issue a loan to MTE for the storage location represented by MTE.
const Loan *L = createLoan(FactMgr, MTE);
- OriginList *List = getOriginsList(*MTE);
CurrentBlockFacts.push_back(
- FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
+ FactMgr.createFact<IssueFact>(L->getID(), OuterMTEID));
}
- flow(MTEList, SubExprList, /*Kill=*/true);
}
void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index c80556715fedf..ca3f239af540c 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -14,7 +14,6 @@ struct [[gsl::Owner]] MyObj {
MyObj operator+(MyObj);
View getView() const [[clang::lifetimebound]];
- const int* getData() const [[clang::lifetimebound]];
};
struct [[gsl::Owner]] MyTrivialObj {
@@ -26,10 +25,6 @@ struct [[gsl::Pointer()]] View {
View(const MyTrivialObj &); // Borrows from MyTrivialObj
View();
void use() const;
-
- const MyObj* data() const;
- const MyObj& operator*() const;
- const MyObj* operator->() const;
};
class TriviallyDestructedClass {
@@ -1460,76 +1455,6 @@ void bar() {
}
}
-namespace DereferenceViews {
-const MyObj& testDeref(MyObj obj) {
- View v = obj; // expected-warning {{address of stack memory is returned later}}
- return *v; // expected-note {{returned here}}
-}
-const MyObj* testDerefAddr(MyObj obj) {
- View v = obj; // expected-warning {{address of stack memory is returned later}}
- return &*v; // expected-note {{returned here}}
-}
-const MyObj* testData(MyObj obj) {
- View v = obj; // expected-warning {{address of stack memory is returned later}}
- return v.data(); // expected-note {{returned here}}
-}
-const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
- View v = obj; // expected-warning {{address of stack memory is returned later}}
- const MyObj* ptr = v.data();
- return ptr->getData(); // expected-note {{returned here}}
-}
-const int* testLifetimeboundAccessorOfMyObjThroughDeref(MyObj obj) {
- View v = obj; // expected-warning {{address of stack memory is returned later}}
- return v->getData(); // expected-note {{returned here}}
-}
-} // namespace DereferenceViews
-
-namespace ViewsBeginEndIterators {
-template <typename T>
-struct [[gsl::Pointer]] Iterator {
- Iterator operator++();
- T& operator*() const;
- T* operator->() const;
- bool operator!=(const Iterator& other) const;
-};
-
-template <typename T>
-struct [[gsl::Owner]] Container {
-using It = Iterator<T>;
-It begin() const [[clang::lifetimebound]];
-It end() const [[clang::lifetimebound]];
-};
-
-MyObj Global;
-
-const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
- for (const MyObj& x : c) { // expected-warning {{address of stack memory is returned later}}
- return x; // expected-note {{returned here}}
- }
- return Global;
-}
-
-View ContainerMyObjReturnView(Container<MyObj> c) {
- for (const MyObj& x : c) { // expected-warning {{address of stack memory is returned later}}
- return x; // expected-note {{returned here}}
- }
- for (View x : c) { // expected-warning {{address of stack memory is returned later}}
- return x; // expected-note {{returned here}}
- }
- return Global;
-}
-
-View ContainerViewsOk(Container<View> c) {
- for (View x : c) {
- return x;
- }
- for (const View& x : c) {
- return x;
- }
- return Global;
-}
-} // namespace ViewsBeginEndIterators
-
namespace reference_type_decl_ref_expr {
struct S {
S();
@@ -1691,3 +1616,25 @@ void test() {
}
} // namespace attr_on_template_params
+
+namespace non_trivial_views {
+struct [[gsl::Pointer]] View {
+ View(const std::string&);
+ ~View(); // Forces a CXXBindTemporaryExpr.
+};
+
+View test1(std::string a) {
+ // Make sure we handle CXXBindTemporaryExpr of view types.
+ return View(a); // expected-warning {{address of stack memory is returned later}} expected-note {{returned here}}
+}
+
+View test2(std::string a) {
+ View b = View(a); // expected-warning {{address of stack memory is returned later}}
+ return b; // expected-note {{returned here}}
+}
+
+View test3(std::string a) {
+ const View& b = View(a); // expected-warning {{address of stack memory is returned later}}
+ return b; // expected-note {{returned here}}
+}
+} // namespace non_trivial_views
More information about the llvm-branch-commits
mailing list