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/2] [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/2] 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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to