https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/178000
>From 2c564fc48fc4c0409ecd6664cfde4fb9d57b4acb Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Mon, 26 Jan 2026 20:27:33 +0300 Subject: [PATCH 1/4] [Lifetime] Fix lifetimebound ignored on template method definition --- .../LifetimeSafety/LifetimeAnnotations.cpp | 17 ++++++++---- .../Sema/warn-lifetime-analysis-nocfg.cpp | 26 +++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 93c7a86d0a57c..dd925d2b8fe6e 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -74,11 +74,18 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { FD = getDeclWithMergedLifetimeBoundAttrs(FD); // Attribute merging doesn't work well with attributes on function types (like // 'this' param). We need to check all redeclarations. - for (const FunctionDecl *Redecl : FD->redecls()) { - const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo(); - if (TSI && getLifetimeBoundAttrFromFunctionType(*TSI)) - return true; - } + auto CheckRedecls = [](const FunctionDecl *F) { + return llvm::any_of(F->redecls(), [](const FunctionDecl *Redecl) { + const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo(); + return TSI && getLifetimeBoundAttrFromFunctionType(*TSI); + }); + }; + + if (CheckRedecls(FD)) + return true; + if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern(); + Pattern && CheckRedecls(Pattern)) + return true; return isNormalAssignmentOperator(FD); } diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 99a796c360a7f..605479d4db372 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1252,6 +1252,24 @@ inline const char* StringTemplateSpecC<char>::data() const [[clang::lifetimeboun return buffer; } +template<typename T> +class MemberFuncsTpl { +public: + // Template Version A: Attribute on declaration only + const T* memberA(const T& x [[clang::lifetimebound]]); + // Template Version B: Attribute on definition only + const T* memberB(const T& x); + // Template Version C: Attribute on BOTH declaration and definition + const T* memberC(const T& x [[clang::lifetimebound]]); +}; + +template<typename T> +const T* MemberFuncsTpl<T>::memberA(const T& x) { return &x; } +template<typename T> +const T* MemberFuncsTpl<T>::memberB(const T& x [[clang::lifetimebound]]) { return &x; } +template<typename T> +const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { return &x; } + void test() { // Non-templated tests const auto ptrA = StringA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} @@ -1260,13 +1278,17 @@ void test() { // Templated tests (generic templates) const auto ptrTA = StringTemplateA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} - // FIXME: Definition is not instantiated until the end of TU. The attribute is not merged when this call is processed. - const auto ptrTB = StringTemplateB<char>().data(); // Definition-only attribute + const auto ptrTB = StringTemplateB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTC = StringTemplateC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} // Template specialization tests const auto ptrTSA = StringTemplateSpecA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSB = StringTemplateSpecB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} + + MemberFuncsTpl<int> mtf; + const int* pTMA = mtf.memberA(1); // Declaration-only attribute // expected-warning {{temporary whose address is used}} + const int* pTMB = mtf.memberB(2); // Definition-only attribute // FIXME: Definition-only attribute + const int* pTMC = mtf.memberC(3); // Both have attribute // expected-warning {{temporary whose address is used}} } } // namespace GH175391 >From 5eb6ef191ec4d0c8d81d531bf094c7c674cbc3c6 Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Mon, 26 Jan 2026 22:51:56 +0300 Subject: [PATCH 2/4] add new case --- clang/test/Sema/warn-lifetime-safety.cpp | 43 +++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index a3ff4cd99288b..2bad0960e7fed 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify=expected,function %s -// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify %s +// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=expected,inference %s #include "Inputs/lifetime-analysis.h" @@ -1575,3 +1575,44 @@ std::string_view& refViewMemberReturnView2(RefMember a) { return a.view; } // ex std::string_view refViewMemberReturnRefView1(RefMember a) { return a.view_ref; } std::string_view& refViewMemberReturnRefView2(RefMember a) { return a.view_ref; } } // namespace field_access + +namespace attr_on_template_params { +struct MyObj { + ~MyObj(); +}; + +template <typename T> +struct MemberFuncsTpl { + ~MemberFuncsTpl(); + // Template Version A: Attribute on declaration only + const T* memberA(const T& x [[clang::lifetimebound]]); + // Template Version B: Attribute on definition only + const T* memberB(const T& x); + // Template Version C: Attribute on BOTH declaration and definition + const T* memberC(const T& x [[clang::lifetimebound]]); +}; + +template <typename T> +const T* MemberFuncsTpl<T>::memberA(const T& x) { + return &x; +} +template <typename T> +const T* MemberFuncsTpl<T>::memberB(const T& x [[clang::lifetimebound]]) { + return &x; +} +template <typename T> +const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { + return &x; +} + +void test() { + MemberFuncsTpl<MyObj> mtf; + const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} + const MyObj* pTMB = mtf.memberB(MyObj()); // inference-warning {{object whose reference is captured does not live long enough}} // inference-note {{destroyed here}} + const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} + (void)pTMA; // expected-note {{later used here}} + (void)pTMB; // inference-note {{later used here}} + (void)pTMC; // expected-note {{later used here}} +} + +} // namespace attr_on_template_params \ No newline at end of file >From 617679bff5262a7dc6726219a2cce5653569baf7 Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Mon, 26 Jan 2026 23:12:52 +0300 Subject: [PATCH 3/4] add newline and remove --- .../Sema/warn-lifetime-analysis-nocfg.cpp | 23 ------------------- clang/test/Sema/warn-lifetime-safety.cpp | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 605479d4db372..29554f5a98029 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1252,24 +1252,6 @@ inline const char* StringTemplateSpecC<char>::data() const [[clang::lifetimeboun return buffer; } -template<typename T> -class MemberFuncsTpl { -public: - // Template Version A: Attribute on declaration only - const T* memberA(const T& x [[clang::lifetimebound]]); - // Template Version B: Attribute on definition only - const T* memberB(const T& x); - // Template Version C: Attribute on BOTH declaration and definition - const T* memberC(const T& x [[clang::lifetimebound]]); -}; - -template<typename T> -const T* MemberFuncsTpl<T>::memberA(const T& x) { return &x; } -template<typename T> -const T* MemberFuncsTpl<T>::memberB(const T& x [[clang::lifetimebound]]) { return &x; } -template<typename T> -const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { return &x; } - void test() { // Non-templated tests const auto ptrA = StringA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} @@ -1285,10 +1267,5 @@ void test() { const auto ptrTSA = StringTemplateSpecA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSB = StringTemplateSpecB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} - - MemberFuncsTpl<int> mtf; - const int* pTMA = mtf.memberA(1); // Declaration-only attribute // expected-warning {{temporary whose address is used}} - const int* pTMB = mtf.memberB(2); // Definition-only attribute // FIXME: Definition-only attribute - const int* pTMC = mtf.memberC(3); // Both have attribute // expected-warning {{temporary whose address is used}} } } // namespace GH175391 diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 2bad0960e7fed..53851cd623933 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1615,4 +1615,4 @@ void test() { (void)pTMC; // expected-note {{later used here}} } -} // namespace attr_on_template_params \ No newline at end of file +} // namespace attr_on_template_params >From de3aaa73ae4c5de1eb2adf0c49b14859b60cb3d6 Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Tue, 27 Jan 2026 12:54:27 +0300 Subject: [PATCH 4/4] inference -> tu in tests --- clang/test/Sema/warn-lifetime-safety.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 53851cd623933..d6064dea9e545 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify=expected,function %s -// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=expected,inference %s +// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=expected,tu %s #include "Inputs/lifetime-analysis.h" @@ -1608,10 +1608,10 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { void test() { MemberFuncsTpl<MyObj> mtf; const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} - const MyObj* pTMB = mtf.memberB(MyObj()); // inference-warning {{object whose reference is captured does not live long enough}} // inference-note {{destroyed here}} + const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} // tu-note {{destroyed here}} const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} (void)pTMA; // expected-note {{later used here}} - (void)pTMB; // inference-note {{later used here}} + (void)pTMB; // tu-note {{later used here}} (void)pTMC; // expected-note {{later used here}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
