[clang] [LifetimeSafety] Propagate loans through pointer arithmetic (PR #189546)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 31 02:15:58 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/189546
>From 364fa6f3020e2d11c2e0d40e995ab7b4c3ce23a3 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Tue, 31 Mar 2026 00:21:57 +0300
Subject: [PATCH 1/5] [LifetimeSafety] propogate loans on pointer arithmetic
---
.../Analyses/LifetimeSafety/FactsGenerator.h | 2 ++
.../lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 14 ++++++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index dfcbdc7d73007..1bfd3fd5f2c1c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -61,6 +61,8 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
+ void handlePointerArithmetic(const BinaryOperator *BO);
+
void handleCXXCtorInitializer(const CXXCtorInitializer *CII);
void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 6b61d7fd64fd7..29f4204345586 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -371,9 +371,19 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
}
+void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
+ if (Expr *RHS = BO->getRHS(); RHS->getType()->isPointerType()) {
+ flowOrigin(*BO, *RHS);
+ return;
+ }
+ Expr *LHS = BO->getLHS();
+ assert(LHS->getType()->isPointerType() && "Unexpected operand was found");
+ flowOrigin(*BO, *LHS);
+}
+
void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
- // TODO: Handle pointer arithmetic (e.g., `p + 1` or `1 + p`) where the
- // result should have the same loans as the pointer operand.
+ if (BO->getType()->isPointerType() && BO->isAdditiveOp())
+ handlePointerArithmetic(BO);
if (BO->isCompoundAssignmentOp())
return;
handleUse(BO->getRHS());
>From 851993ed9f98b26e86ab028d09d7dc20bd9d91d4 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Tue, 31 Mar 2026 09:52:53 +0300
Subject: [PATCH 2/5] [LifetimeSfety] move check after compund assignment check
and add tests
---
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 5 +++--
clang/test/Sema/warn-lifetime-safety-suggestions.cpp | 7 +++++++
clang/test/Sema/warn-lifetime-safety.cpp | 7 +++----
3 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 29f4204345586..7db01392cc11f 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -382,10 +382,11 @@ void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
}
void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
- if (BO->getType()->isPointerType() && BO->isAdditiveOp())
- handlePointerArithmetic(BO);
if (BO->isCompoundAssignmentOp())
return;
+
+ if (BO->getType()->isPointerType() && BO->isAdditiveOp())
+ handlePointerArithmetic(BO);
handleUse(BO->getRHS());
if (BO->isAssignmentOp())
handleAssignment(BO->getLHS(), BO->getRHS());
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 22c4222022ebf..3591270306827 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -438,4 +438,11 @@ struct MemberArrayReturn {
}
};
+struct MemberPointerArithmeticReturn {
+ int arr[10];
+ int* end() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return arr + 10; // expected-note {{param returned here}}
+ }
+};
+
} // namespace array
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 76d43445f8636..4e2c69c7fb2b8 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2070,14 +2070,13 @@ int* static_array() {
return &a[1];
}
-// FIXME: Pointer arithmetic is not yet tracked.
void pointer_arithmetic_use_after_scope() {
int* p;
{
int a[10]{};
- p = a + 5;
- }
- (void)*p; // Should warn.
+ p = a + 5; // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
}
// FIXME: Copying a pointer value out of an array element is not tracked.
>From edece21a757b81e91e706d7224258e33e087a3d0 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Tue, 31 Mar 2026 09:55:30 +0300
Subject: [PATCH 3/5] move test into existing struct
---
clang/test/Sema/warn-lifetime-safety-suggestions.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 3591270306827..d776c1067e4d0 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -436,13 +436,9 @@ struct MemberArrayReturn {
int* getData() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
return arr; // expected-note {{param returned here}}
}
-};
-
-struct MemberPointerArithmeticReturn {
- int arr[10];
- int* end() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
- return arr + 10; // expected-note {{param returned here}}
- }
+ int* getLast() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return arr + 10; // expected-note {{param returned here}}
+ }
};
} // namespace array
>From d355960fa51d80422325c9e50eaf4314b58f57bb Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Tue, 31 Mar 2026 09:58:50 +0300
Subject: [PATCH 4/5] update assert message
---
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 7db01392cc11f..01cdba1e1ae31 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -377,14 +377,14 @@ void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
return;
}
Expr *LHS = BO->getLHS();
- assert(LHS->getType()->isPointerType() && "Unexpected operand was found");
+ assert(LHS->getType()->isPointerType() &&
+ "Pointer arithmetic must have a pointer operand");
flowOrigin(*BO, *LHS);
}
void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
if (BO->isCompoundAssignmentOp())
return;
-
if (BO->getType()->isPointerType() && BO->isAdditiveOp())
handlePointerArithmetic(BO);
handleUse(BO->getRHS());
>From 4e2061f81bf5433ee747c027c277fb01e80abafd Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Tue, 31 Mar 2026 12:15:27 +0300
Subject: [PATCH 5/5] switch to killAndFlowOrigin, test for subtraction
---
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 4 ++--
clang/test/Sema/warn-lifetime-safety.cpp | 9 ++++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 01cdba1e1ae31..68017220e0a54 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -373,13 +373,13 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
if (Expr *RHS = BO->getRHS(); RHS->getType()->isPointerType()) {
- flowOrigin(*BO, *RHS);
+ killAndFlowOrigin(*BO, *RHS);
return;
}
Expr *LHS = BO->getLHS();
assert(LHS->getType()->isPointerType() &&
"Pointer arithmetic must have a pointer operand");
- flowOrigin(*BO, *LHS);
+ killAndFlowOrigin(*BO, *LHS);
}
void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 4e2c69c7fb2b8..7158f68ee1727 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2072,11 +2072,14 @@ int* static_array() {
void pointer_arithmetic_use_after_scope() {
int* p;
+ int* p2;
{
int a[10]{};
- p = a + 5; // expected-warning {{object whose reference is captured does not live long enough}}
- } // expected-note {{destroyed here}}
- (void)*p; // expected-note {{later used here}}
+ p = a + 5; // expected-warning {{object whose reference is captured does not live long enough}}
+ p2 = a - 5; // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+ (void)*p2; // expected-note {{later used here}}
}
// FIXME: Copying a pointer value out of an array element is not tracked.
More information about the cfe-commits
mailing list