https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/189907
>From 4f237572faa6ba6320b95ef5e6095ed4076e22c0 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Tue, 31 Mar 2026 11:56:30 +0300 Subject: [PATCH 1/6] [LifetimeSafety] apply basic fix and update the test --- clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 5 +++++ clang/test/Sema/warn-lifetime-analysis-nocfg.cpp | 12 +++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 861e1f6fa8a33..b55eeb59ac34d 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -712,6 +712,11 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); KillSrc = false; + } else if (IsArgLifetimeBound(I)) { + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), + KillSrc)); + KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { assert(ArgList->getLength() >= 2 && diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index d58f23e4b554c..0ed151b9db136 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -931,13 +931,11 @@ struct [[gsl::Pointer]] Pointer { Pointer(const Bar & bar [[clang::lifetimebound]]); }; Pointer test3(Bar bar) { - // FIXME: Detect this using the CFG-based lifetime analysis (constructor of a pointer). - // https://github.com/llvm/llvm-project/issues/175898 - Pointer p = Pointer(Bar()); // expected-warning {{temporary}} - use(p); - p = Pointer(Bar()); // expected-warning {{object backing}} - use(p); - return bar; // expected-warning {{address of stack}} + Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + use(p); // cfg-note {{later used here}} + p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + use(p); // cfg-note {{later used here}} + return bar; // expected-warning {{address of stack}} cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}} } template<typename T> >From 610a9a48d4ccc0da8a293d4e60a3391b288caafe Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Wed, 1 Apr 2026 10:58:02 +0300 Subject: [PATCH 2/6] combine checks into one, update the comment --- .../lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b55eeb59ac34d..b314ac45c5760 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -704,15 +704,10 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2); ArgList = getRValueOrigins(Args[I], ArgList); } - if (isGslOwnerType(Args[I]->getType())) { - // The constructed gsl::Pointer borrows from the Owner's storage, not - // from what the Owner itself borrows, so only the outermost origin is - // needed. - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), - KillSrc)); - KillSrc = false; - } else if (IsArgLifetimeBound(I)) { + if (isGslOwnerType(Args[I]->getType()) || IsArgLifetimeBound(I)) { + // The constructed gsl::Pointer is dependent on the argument itself, not + // on anything the argument is dependent on, so only the outermost + // origin is needed. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); >From 9e5130e89e4bcf3f2302ed05f133f4ae40cdd1d7 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Wed, 1 Apr 2026 19:28:22 +0300 Subject: [PATCH 3/6] move check to a different branch --- .../lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b314ac45c5760..75185be0ca203 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -704,14 +704,20 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2); ArgList = getRValueOrigins(Args[I], ArgList); } - if (isGslOwnerType(Args[I]->getType()) || IsArgLifetimeBound(I)) { - // The constructed gsl::Pointer is dependent on the argument itself, not - // on anything the argument is dependent on, so only the outermost - // origin is needed. + if (isGslOwnerType(Args[I]->getType())) { + // The constructed gsl::Pointer borrows from the Owner's storage, not + // from what the Owner itself borrows, so only the outermost origin is + // needed. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); KillSrc = false; + } else if (IsArgLifetimeBound(I)) { + // A lifetimebound argument may affect the result at every origin level, + // so we propagate the whole OriginList instead of only the outermost + // origin. + flow(CallList, ArgList, KillSrc); + KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { assert(ArgList->getLength() >= 2 && >From 1ce95bb35ac2c7cba6d8323d227eeb0792362ed4 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Thu, 2 Apr 2026 11:52:35 +0300 Subject: [PATCH 4/6] add a few more tests --- clang/test/Sema/warn-lifetime-safety.cpp | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 88a502491e6c0..c18c852eb0703 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2413,3 +2413,49 @@ void owner_outlives_lifetimebound_source() { } } // namespace track_origins_for_lifetimebound_record_type + +namespace gslpointer_construction_from_lifetimebound { + +struct Bar {}; +template <typename T> struct [[gsl::Pointer]] Pointer { + Pointer(); + Pointer(const T &bar [[clang::lifetimebound]]); + Pointer(const Pointer<T> &p [[clang::lifetimebound]]); + const T &operator*() const [[clang::lifetimebound]]; +}; + +template <typename T> void use(T); + +void local_pointer() { + Pointer<int> p; + { + int v{}; + p = Pointer(v); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + use(*p); // expected-note {{later used here}} +} + +void nested_local_pointer() { + Pointer<Pointer<Pointer<Bar>>> ppp; + Pointer<Pointer<Bar>> pp; + Pointer<Bar> p; + { + Bar v{}; + p = Pointer(v); // expected-warning {{object whose reference is captured does not live long enough}} + pp = Pointer(p); + ppp = Pointer(pp); + } // expected-note {{destroyed here}} + use(***ppp); // expected-note {{later used here}} +} + +struct PFieldFromParam { + Pointer<Bar> value; // function-note {{this field dangles}} + PFieldFromParam(Bar bar) : value(bar) {} // function-warning {{address of stack memory escapes to a field}} +}; + +struct PFieldFromTemp { + Pointer<Bar> value; // function-note {{this field dangles}} + PFieldFromTemp() : value(Bar{}) {} // function-warning {{address of stack memory escapes to a field}} +}; + +} // namespace gslpointer_construction_from_lifetimebound >From b3a8a1e6804431f5990d35b31308e18948fb577d Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Thu, 2 Apr 2026 11:53:48 +0300 Subject: [PATCH 5/6] add github link to the issue --- clang/test/Sema/warn-lifetime-safety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index c18c852eb0703..5603ff81966e6 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2415,7 +2415,7 @@ void owner_outlives_lifetimebound_source() { } // namespace track_origins_for_lifetimebound_record_type namespace gslpointer_construction_from_lifetimebound { - +// https://github.com/llvm/llvm-project/issues/175898 struct Bar {}; template <typename T> struct [[gsl::Pointer]] Pointer { Pointer(); >From ab04bd2952eea7bc166415f5806c496034c93bcc Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Fri, 3 Apr 2026 09:33:43 +0300 Subject: [PATCH 6/6] remove annotations from copy ctor --- clang/test/Sema/warn-lifetime-safety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 5603ff81966e6..20357e68dd7ad 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2420,7 +2420,7 @@ struct Bar {}; template <typename T> struct [[gsl::Pointer]] Pointer { Pointer(); Pointer(const T &bar [[clang::lifetimebound]]); - Pointer(const Pointer<T> &p [[clang::lifetimebound]]); + Pointer(const Pointer<T> &p); const T &operator*() const [[clang::lifetimebound]]; }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
