[clang] [LifetimeSafety] resolved lifetimeBound violation in constructor (PR #204797)
Jiaqi He via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 22 04:35:35 PDT 2026
https://github.com/heturing updated https://github.com/llvm/llvm-project/pull/204797
>From d765d180a57d1fc9d92a35feebb0cedb76d25c8f Mon Sep 17 00:00:00 2001
From: Jiaqi He <heturing at gmail.com>
Date: Fri, 19 Jun 2026 18:48:23 +0800
Subject: [PATCH 1/2] [LifetimeSafety] resolved lifetimeBound violation in
constructor
---
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 5 ++-
.../Sema/LifetimeSafety/lifetimebound.cpp | 35 +++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Sema/LifetimeSafety/lifetimebound.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index d41d6f43f837b..595f9a1d32eba 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -135,7 +135,10 @@ class LifetimeChecker {
return;
if (PVD->hasAttr<LifetimeBoundAttr>()) {
// Track that this lifetimebound parameter correctly escapes.
- if (isa<ReturnEscapeFact>(OEF))
+ bool isVerifiedEscape =
+ isa<ReturnEscapeFact>(OEF) ||
+ (isa<FieldEscapeFact>(OEF) && isa<CXXConstructorDecl>(FD));
+ if (isVerifiedEscape)
VerifiedLiftimeboundEscapes.insert(PVD);
} else {
// Otherwise, suggest lifetimebound for parameter escaping through
diff --git a/clang/test/Sema/LifetimeSafety/lifetimebound.cpp b/clang/test/Sema/LifetimeSafety/lifetimebound.cpp
new file mode 100644
index 0000000000000..a5e26b5bdadf2
--- /dev/null
+++ b/clang/test/Sema/LifetimeSafety/lifetimebound.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-all -verify %s
+
+
+using size_t = decltype(sizeof(0));
+extern "C" size_t strlen(const char *);
+
+#define LIFETIMEBOUND [[clang::lifetimebound]]
+
+struct View
+{
+ View(const char* data LIFETIMEBOUND)
+ : mData(data)
+ , mSize(strlen(data))
+ {}
+
+ const char* data() const {
+ return mData;
+ }
+
+ size_t size() const {
+ return mSize;
+ }
+
+private:
+ const char* mData;
+ size_t mSize;
+};
+
+void test() {
+ char *c = new char[5]; //expected-warning {{allocated object does not live long enough}}
+ View v(c);
+ delete[] c; // expected-note {{freed here}}
+ const char *c1 = v.data(); // expected-note {{later used here}}
+ return;
+}
\ No newline at end of file
>From 49f779b9e19dbfdf96b166539c1f7fc84909e33a Mon Sep 17 00:00:00 2001
From: Jiaqi He <heturing at gmail.com>
Date: Mon, 22 Jun 2026 16:05:51 +0800
Subject: [PATCH 2/2] refactor code and test case
---
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 9 +++--
.../Sema/LifetimeSafety/lifetimebound.cpp | 35 -------------------
.../misplaced-lifetimebound-intra-tu.cpp | 5 +++
3 files changed, 9 insertions(+), 40 deletions(-)
delete mode 100644 clang/test/Sema/LifetimeSafety/lifetimebound.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 595f9a1d32eba..c258a1dc3596c 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -134,11 +134,10 @@ class LifetimeChecker {
if (IsMoved)
return;
if (PVD->hasAttr<LifetimeBoundAttr>()) {
- // Track that this lifetimebound parameter correctly escapes.
- bool isVerifiedEscape =
- isa<ReturnEscapeFact>(OEF) ||
- (isa<FieldEscapeFact>(OEF) && isa<CXXConstructorDecl>(FD));
- if (isVerifiedEscape)
+ // Track that this lifetimebound parameter correctly escapes
+ // (via return or via field assignment in a constructor).
+ if (isa<ReturnEscapeFact>(OEF) ||
+ (isa<FieldEscapeFact>(OEF) && isa<CXXConstructorDecl>(FD)))
VerifiedLiftimeboundEscapes.insert(PVD);
} else {
// Otherwise, suggest lifetimebound for parameter escaping through
diff --git a/clang/test/Sema/LifetimeSafety/lifetimebound.cpp b/clang/test/Sema/LifetimeSafety/lifetimebound.cpp
deleted file mode 100644
index a5e26b5bdadf2..0000000000000
--- a/clang/test/Sema/LifetimeSafety/lifetimebound.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-all -verify %s
-
-
-using size_t = decltype(sizeof(0));
-extern "C" size_t strlen(const char *);
-
-#define LIFETIMEBOUND [[clang::lifetimebound]]
-
-struct View
-{
- View(const char* data LIFETIMEBOUND)
- : mData(data)
- , mSize(strlen(data))
- {}
-
- const char* data() const {
- return mData;
- }
-
- size_t size() const {
- return mSize;
- }
-
-private:
- const char* mData;
- size_t mSize;
-};
-
-void test() {
- char *c = new char[5]; //expected-warning {{allocated object does not live long enough}}
- View v(c);
- delete[] c; // expected-note {{freed here}}
- const char *c1 = v.data(); // expected-note {{later used here}}
- return;
-}
\ No newline at end of file
diff --git a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
index 25f5b6e94c28c..a6093a8a0e3cb 100644
--- a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
@@ -214,3 +214,8 @@ struct ConfiguredMacroMember {
const MyObj &ConfiguredMacroMember::configured_this() [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute appears here on the definition}}
return data;
}
+struct ViewWithMember {
+ // No warning. A lifetimebound constructor parameter may escape into a field of the constructed object.
+ ViewWithMember(const char *data [[clang::lifetimebound]]) : mData(data) {}
+ const char *mData;
+};
More information about the cfe-commits
mailing list