https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196926
>From a476f6283ccd236f58b85ca2956b1db4d905439b Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 12:48:36 +0300 Subject: [PATCH 1/5] [LifetimeSafety] Warn on implicit this lifetimebound violations --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 8 +++- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 13 ++++-- clang/lib/Sema/SemaLifetimeSafety.h | 17 ++++++- .../warn-lifetime-safety-lifetimebound.cpp | 45 +++++++++++++++++++ 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 7ccf30ba14987..b4f918a4cb3e7 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -110,7 +110,13 @@ class LifetimeSafetySemaHelper { // Reports misuse of [[clang::lifetimebound]] when parameter doesn't escape // through return. - virtual void reportLifetimeboundViolation(const ParmVarDecl *VD) {} + virtual void + reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) {} + + // Reports misuse of [[clang::lifetimebound]] when implicit this parameter + // doesn't escape through return. + virtual void + reportLifetimeboundViolation(const CXXMethodDecl *MDWithLifetimebound) {} // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 879812f3de0d3..4374ca7235d03 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11007,8 +11007,8 @@ def warn_lifetime_safety_dangling_global_moved InGroup<LifetimeSafetyDanglingGlobalMoved>, DefaultIgnore; -def warn_lifetime_safety_param_lifetimebound_violation - : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'}0">, +def warn_lifetime_safety_lifetimebound_violation + : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'|an implicit this parameter}0">, InGroup<LifetimeSafetyLifetimeboundViolation>, DefaultIgnore; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index fc77ed3097602..f60448b23e6e8 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -60,7 +60,7 @@ class LifetimeChecker { llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap; llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap; - llvm::DenseSet<const ParmVarDecl *> VerifiedLiftimeboundEscapes; + llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes; const LoanPropagationAnalysis &LoanPropagation; const MovedLoansAnalysis &MovedLoans; const LiveOriginsAnalysis &LiveOrigins; @@ -147,9 +147,10 @@ class LifetimeChecker { // field! }; auto CheckImplicitThis = [&](const CXXMethodDecl *MD) { - if (!implicitObjectParamIsLifetimeBound(MD)) - if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) - AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); + if (implicitObjectParamIsLifetimeBound(MD)) + VerifiedLiftimeboundEscapes.insert(MD); + else if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) + AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); }; auto MovedAtEscape = MovedLoans.getMovedLoans(OEF); for (LoanID LID : EscapedLoans) { @@ -366,6 +367,10 @@ class LifetimeChecker { void reportLifetimeboundViolations() { if (!isa<FunctionDecl>(FD)) return; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); + MD && implicitObjectParamIsLifetimeBound(MD) && + !VerifiedLiftimeboundEscapes.contains(MD)) + SemaHelper->reportLifetimeboundViolation(MD); for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) { if (!PVD->hasAttr<LifetimeBoundAttr>()) continue; diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 5b1cf41445399..f104ea5cb0026 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -36,7 +36,7 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) { diag::warn_lifetime_safety_dangling_global, diag::warn_lifetime_safety_dangling_global_moved, diag::warn_lifetime_safety_noescape_escapes, - diag::warn_lifetime_safety_param_lifetimebound_violation, + diag::warn_lifetime_safety_lifetimebound_violation, }; for (unsigned DiagID : DiagIDs) if (!Diags.isIgnored(DiagID, D->getBeginLoc())) @@ -183,10 +183,23 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { StringRef ParamName = ParmWithLifetimebound->getName(); bool HasName = ParamName.size() > 0; S.Diag(ParmWithLifetimebound->getLocation(), - diag::warn_lifetime_safety_param_lifetimebound_violation) + diag::warn_lifetime_safety_lifetimebound_violation) << HasName << ParamName << ParmWithLifetimebound->getSourceRange(); } + void reportLifetimeboundViolation( + const CXXMethodDecl *MDWithLifetimebound) override { + const Stmt *Body = MDWithLifetimebound->getBody(); + assert(Body && "Expected a body"); + // FIXME: When #196549 lands, we can extract the attribute location and warn + // on it, for now warn on everything before the body. + S.Diag(MDWithLifetimebound->getLocation(), + diag::warn_lifetime_safety_lifetimebound_violation) + << 2 << "this" + << CharSourceRange::getCharRange(MDWithLifetimebound->getBeginLoc(), + Body->getBeginLoc()); + } + void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override { diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index 941a3bb8ce1e3..a7caa1e773fe2 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -87,3 +87,48 @@ View annotated_decl_but_not_def_not_returned(const MyObj &obj [[clang::lifetimeb View annotated_decl_but_not_def_not_returned(const MyObj &obj) { // expected-warning {{could not verify that the return value can be lifetime bound to 'obj'}} return not_lb(obj); } + +struct BadThisReturn { + MyObj data; + + View get() const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return not_lb(data); + } +}; + +struct GoodThisReturn { + MyObj data; + + View get() const [[clang::lifetimebound]] { + return data; + } +}; + +// FIXME: Wrong warning loc +struct RedeclaredThis { + MyObj data; + View get() const [[clang::lifetimebound]]; +}; + +View RedeclaredThis::get() const { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return not_lb(data); +} + +struct ThisAndParam { + MyObj data; + + View get(const MyObj &obj [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return lb(obj); + } +}; + +struct ThisAndMixedParams { + MyObj data; + + View get( // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + const MyObj &a [[clang::lifetimebound]], + const MyObj &b, + const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} + return cond() ? lb(a) : not_lb(b); + } +}; >From 137b72977b159012989a2ab161e24f7146d37f04 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 13:55:43 +0300 Subject: [PATCH 2/5] warn on attribute loc instead of def in implicit this lifetimbound --- .../LifetimeSafety/LifetimeAnnotations.h | 5 ++++ .../LifetimeSafety/LifetimeAnnotations.cpp | 28 ++++++++++++------- clang/lib/Sema/SemaLifetimeSafety.h | 14 ++++------ .../warn-lifetime-safety-lifetimebound.cpp | 9 +++--- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h index 0db10f8a58cea..f418f8a5132ec 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h @@ -41,6 +41,11 @@ bool isNormalAssignmentOperator(const FunctionDecl *FD); /// has the lifetimebound attribute. bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD); +/// Returns the lifetimebound attribute for the implicit this parameter, if it +/// exists on any redeclaration. +const LifetimeBoundAttr * +getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD); + /// Returns true if the implicit object parameter (this) should be considered /// lifetimebound, either due to an explicit lifetimebound attribute on the /// method or because it's a normal assignment operator. diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 559188fddc9fa..b4ae00f2b0327 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -72,21 +72,29 @@ getLifetimeBoundAttrFromFunctionType(const TypeSourceInfo &TSI) { return nullptr; } -bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { +const LifetimeBoundAttr * +getImplicitObjectParamLifetimeBoundAttr(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. - auto CheckRedecls = [](const FunctionDecl *F) { - return llvm::any_of(F->redecls(), [](const FunctionDecl *Redecl) { - const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo(); - return TSI && getLifetimeBoundAttrFromFunctionType(*TSI); - }); + auto CheckRedecls = [](const FunctionDecl *F) -> const LifetimeBoundAttr * { + for (const FunctionDecl *Redecl : F->redecls()) { + if (const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo()) + if (const auto *Attr = getLifetimeBoundAttrFromFunctionType(*TSI)) + return Attr; + } + return nullptr; }; - if (CheckRedecls(FD)) - return true; - if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern(); - Pattern && CheckRedecls(Pattern)) + if (const auto *Attr = CheckRedecls(FD)) + return Attr; + if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern()) + return CheckRedecls(Pattern); + return nullptr; +} + +bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { + if (getImplicitObjectParamLifetimeBoundAttr(FD)) return true; return isNormalAssignmentOperator(FD); } diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index f104ea5cb0026..8ea172d1a9624 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H #define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Lex/Lexer.h" @@ -189,15 +190,12 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportLifetimeboundViolation( const CXXMethodDecl *MDWithLifetimebound) override { - const Stmt *Body = MDWithLifetimebound->getBody(); - assert(Body && "Expected a body"); - // FIXME: When #196549 lands, we can extract the attribute location and warn - // on it, for now warn on everything before the body. - S.Diag(MDWithLifetimebound->getLocation(), + const auto *Attr = + getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound); + assert(Attr && "Expected lifetimebound attribute"); + S.Diag(Attr->getLocation(), diag::warn_lifetime_safety_lifetimebound_violation) - << 2 << "this" - << CharSourceRange::getCharRange(MDWithLifetimebound->getBeginLoc(), - Body->getBeginLoc()); + << 2 << "" << Attr->getRange(); } void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index a7caa1e773fe2..1e27091099d04 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -107,10 +107,10 @@ struct GoodThisReturn { // FIXME: Wrong warning loc struct RedeclaredThis { MyObj data; - View get() const [[clang::lifetimebound]]; + View get() const [[clang::lifetimebound]]; // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} }; -View RedeclaredThis::get() const { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} +View RedeclaredThis::get() const { return not_lb(data); } @@ -125,10 +125,11 @@ struct ThisAndParam { struct ThisAndMixedParams { MyObj data; - View get( // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + View get( const MyObj &a [[clang::lifetimebound]], const MyObj &b, - const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} + const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} \ + // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} return cond() ? lb(a) : not_lb(b); } }; >From 69be295afb3e080f0be4418dd775507ff6c9db61 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 13:57:32 +0300 Subject: [PATCH 3/5] revert unrelated change --- .../clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index b4f918a4cb3e7..c3b33fefe8327 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -110,8 +110,7 @@ class LifetimeSafetySemaHelper { // Reports misuse of [[clang::lifetimebound]] when parameter doesn't escape // through return. - virtual void - reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) {} + virtual void reportLifetimeboundViolation(const ParmVarDecl *VD) {} // Reports misuse of [[clang::lifetimebound]] when implicit this parameter // doesn't escape through return. >From 47b05b4e627d7af64eac53e33b952c7c2decc2b7 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 13:59:08 +0300 Subject: [PATCH 4/5] remove fixme --- clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index 1e27091099d04..4aeac3ae21f77 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -104,7 +104,6 @@ struct GoodThisReturn { } }; -// FIXME: Wrong warning loc struct RedeclaredThis { MyObj data; View get() const [[clang::lifetimebound]]; // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} >From 49b7019ec0b2ca528341cb1b1b46f0774b970cf6 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 14:26:30 +0300 Subject: [PATCH 5/5] change to the instead of an --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4374ca7235d03..75df782f3ed4f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11008,7 +11008,7 @@ def warn_lifetime_safety_dangling_global_moved DefaultIgnore; def warn_lifetime_safety_lifetimebound_violation - : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'|an implicit this parameter}0">, + : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'|the implicit this parameter}0">, InGroup<LifetimeSafetyLifetimeboundViolation>, DefaultIgnore; diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index 99254247db634..9c94b58d8b4ac 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -90,7 +90,7 @@ View annotated_decl_but_not_def_not_returned(const MyObj &obj) { struct BadThisReturn { MyObj data; - View get() const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + View get() const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to the implicit this parameter}} return not_lb(data); } }; @@ -105,7 +105,7 @@ struct GoodThisReturn { struct RedeclaredThis { MyObj data; - View get() const [[clang::lifetimebound]]; // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + View get() const [[clang::lifetimebound]]; // expected-warning {{could not verify that the return value can be lifetime bound to the implicit this parameter}} }; View RedeclaredThis::get() const { @@ -115,7 +115,7 @@ View RedeclaredThis::get() const { struct ThisAndParam { MyObj data; - View get(const MyObj &obj [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + View get(const MyObj &obj [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to the implicit this parameter}} return lb(obj); } }; @@ -127,7 +127,7 @@ struct ThisAndMixedParams { const MyObj &a [[clang::lifetimebound]], const MyObj &b, const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} \ - // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + // expected-warning {{could not verify that the return value can be lifetime bound to the implicit this parameter}} return cond() ? lb(a) : not_lb(b); } }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
