[clang] [LifetimeSafety] Implicit lifetimebound only for accessor methods of GSL owners (PR #174741)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 14 06:54:34 PST 2026
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/174741
>From ce2cb8555280945312f4afaca6b72331c3b1477c Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 16 Dec 2025 12:13:42 +0000
Subject: [PATCH] only-for-owners
---
.../LifetimeSafety/FactsGenerator.cpp | 16 ++++++
.../unittests/Analysis/LifetimeSafetyTest.cpp | 52 +++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index fe5fabc6d6405..b10c61f1cb6b7 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -485,6 +485,14 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
}
return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
};
+ auto shouldTrackPointerImplicitObjectArg = [FD](unsigned I) -> bool {
+ const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+ if (!Method || !Method->isInstance())
+ return false;
+ return I == 0 &&
+ isGslPointerType(Method->getFunctionObjectParameterType()) &&
+ shouldTrackImplicitObjectArg(Method);
+ };
if (Args.empty())
return;
bool KillSrc = true;
@@ -505,6 +513,14 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
flow(CallList, ArgList, KillSrc);
KillSrc = false;
}
+ } else if (shouldTrackPointerImplicitObjectArg(I)) {
+ assert(ArgList->getLength() >= 2 &&
+ "Object arg of pointer type should have atleast two origins");
+ // See through the GSLPointer reference to see the pointer's value.
+ CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+ CallList->getOuterOriginID(),
+ ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
+ KillSrc = false;
} else if (IsArgLifetimeBound(I)) {
// Lifetimebound on a non-GSL-ctor function means the returned
// pointer/reference itself must not outlive the arguments. This
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 3afb44656d78a..f4eb5b22610e4 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -1751,6 +1751,58 @@ TEST_F(LifetimeAnalysisTest, TrackImplicitObjectArg_MapFind) {
EXPECT_THAT(Origin("it"), HasLoansTo({"m"}, "p1"));
}
+
+TEST_F(LifetimeAnalysisTest, TrackImplicitObjectArg_GSLPointerArg) {
+ SetupTest(R"(
+ namespace std {
+
+ template<typename T>
+ struct basic_string_view {
+ basic_string_view();
+ basic_string_view(const T *);
+ const T *begin() const;
+ const T *data() const;
+ };
+ using string_view = basic_string_view<char>;
+
+ template<typename T>
+ struct basic_string {
+ basic_string();
+ basic_string(const T *);
+ const T *c_str() const;
+ operator basic_string_view<T> () const;
+ const T *data() const;
+ };
+ using string = basic_string<char>;
+ }
+
+ void target() {
+ std::string s1;
+ std::string_view sv1 = s1;
+
+ std::string s2;
+ const char* sv2 = std::string_view(s2).begin();
+
+ std::string s3;
+ const char* sv3 = std::string_view(s3).data();
+
+ std::string s4;
+ std::string_view sv4 = std::string_view{std::string_view(s4).data()};
+
+ std::string s5;
+ const char* data5 = std::string_view(s5).data();
+ std::string_view sv5 = data5;
+ POINT(end);
+ }
+ )");
+ EXPECT_THAT(Origin("sv1"), HasLoansTo({"s1"}, "end"));
+ EXPECT_THAT(Origin("sv2"), HasLoansTo({"s2"}, "end"));
+ EXPECT_THAT(Origin("sv3"), HasLoansTo({"s3"}, "end"));
+ // FIXME: Handle GSL pointer construction from raw pointers.
+ EXPECT_THAT(Origin("sv4"), HasLoansTo({}, "end"));
+ EXPECT_THAT(Origin("sv5"), HasLoansTo({}, "end"));
+}
+
// ========================================================================= //
// Tests for shouldTrackFirstArgument
// ========================================================================= //
More information about the cfe-commits
mailing list