[clang] 3278ff7 - [LifetimeSafety] Fix 'clang::lifetimebound' ignored on template method definition (#178000)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 27 02:17:59 PST 2026
Author: Baranov Victor
Date: 2026-01-27T10:17:53Z
New Revision: 3278ff7a4fa8f90c60081c88f3d441cf1468e84c
URL: https://github.com/llvm/llvm-project/commit/3278ff7a4fa8f90c60081c88f3d441cf1468e84c
DIFF: https://github.com/llvm/llvm-project/commit/3278ff7a4fa8f90c60081c88f3d441cf1468e84c.diff
LOG: [LifetimeSafety] Fix 'clang::lifetimebound' ignored on template method definition (#178000)
Closes https://github.com/llvm/llvm-project/issues/177798.
Same pattern with `getTemplateInstantiationPattern()` for attrs checking
is already used in other LLVM places, e.g.:
https://github.com/llvm/llvm-project/blob/b19238d0d0a7d026de8e2ad28775db57afccb01d/clang/lib/CodeGen/CodeGenModule.cpp#L2830-L2839
Added:
Modified:
clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
clang/test/Sema/warn-lifetime-safety.cpp
Removed:
################################################################################
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..29554f5a98029 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1260,8 +1260,7 @@ 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
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index a3ff4cd99288b..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 %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"
@@ -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()); // 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; // tu-note {{later used here}}
+ (void)pTMC; // expected-note {{later used here}}
+}
+
+} // namespace attr_on_template_params
More information about the cfe-commits
mailing list