https://github.com/iitianpushkar updated https://github.com/llvm/llvm-project/pull/206337
>From 0798fec97660d9bfa086d157ded8bf070e48b7c5 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Sun, 28 Jun 2026 12:04:08 +0530 Subject: [PATCH 1/6] added note for lifetimebound mark --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7e20630708312..a024e4cebfdcc 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11069,6 +11069,7 @@ def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global st def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">; def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">; def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">; +def note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the storage of %1" because %2 is marked [[clang::lifetimebound]]">; def warn_lifetime_safety_intra_tu_param_suggestion : Warning<"parameter in intra-TU function should be marked " >From b7dc4cdf3e6f8bdb92250f00e9c9940c5947a1d4 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Sun, 28 Jun 2026 13:20:31 +0530 Subject: [PATCH 2/6] added lifetimeboundinfo struct --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index 5c671a93b149c..1e0ef8680f7db 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -131,24 +131,37 @@ class ExpireFact : public Fact { }; class OriginFlowFact : public Fact { +public: + struct LifetimeBoundInfo { + const Expr *Call = nullptr; + const ParmVarDecl *Param = nullptr; + bool isImplicitObject = false; + }; + +private: OriginID OIDDest; OriginID OIDSrc; // True if the destination origin should be killed (i.e., its current loans // cleared) before the source origin's loans are flowed into it. bool KillDest; + std::optional<LifetimeBoundInfo> LifetimeBound; public: static bool classof(const Fact *F) { return F->getKind() == Kind::OriginFlow; } - OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest) + OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest, + std::optional<LifetimeBoundInfo> LifetimeBound = std::nullopt) : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc), - KillDest(KillDest) {} + KillDest(KillDest), LifetimeBound(LifetimeBound) {} OriginID getDestOriginID() const { return OIDDest; } OriginID getSrcOriginID() const { return OIDSrc; } bool getKillDest() const { return KillDest; } + std::optional<LifetimeBoundInfo> getLifetimeBoundInfo() const { + return LifetimeBound; + } void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override; >From 84acd32e64085ef3dfd580658f222d97a9801240 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Sun, 28 Jun 2026 18:18:09 +0530 Subject: [PATCH 3/6] added full flow to include lifetimebound reason in alias note --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 9 +++- .../Analyses/LifetimeSafety/LoanPropagation.h | 14 ++++++ .../clang/Basic/DiagnosticSemaKinds.td | 3 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 26 ++++++---- clang/lib/Analysis/LifetimeSafety/Facts.cpp | 2 + .../LifetimeSafety/FactsGenerator.cpp | 39 +++++++++++---- .../LifetimeSafety/LoanPropagation.cpp | 46 ++++++++++++++++-- clang/lib/Sema/SemaLifetimeSafety.h | 47 +++++++++++++++---- 8 files changed, 151 insertions(+), 35 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 80a23f18baebd..c177e9e222bdc 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -49,6 +49,12 @@ enum class WarningScope { IntraTU // For warnings on functions local to a Translation Unit. }; +struct LifetimeSafetyAliasChainEntry { + const Expr *E = nullptr; + const ParmVarDecl *LifetimeBoundParam = nullptr; + bool LifetimeBoundImplicitObject = false; +}; + /// Abstract interface for operations requiring Sema access. /// /// This class exists to break a circular dependency: the LifetimeSafety @@ -67,7 +73,8 @@ class LifetimeSafetySemaHelper { virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, - llvm::ArrayRef<const Expr *> ExprChain) {} + llvm::ArrayRef<LifetimeSafetyAliasChainEntry> + AliasChain) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h index 724c6eee7d3c2..92d69f99c8794 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h @@ -29,6 +29,11 @@ namespace clang::lifetimes::internal { using LoanSet = llvm::ImmutableSet<LoanID>; using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>; +struct OriginFlowChainStep { + OriginID OID; + const OriginFlowFact *FlowFact; +}; + class LoanPropagationAnalysis { public: LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, @@ -51,6 +56,15 @@ class LoanPropagationAnalysis { llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const; + llvm::SmallVector<OriginFlowChainStep> + buildOriginFlowChainWithFacts(ProgramPoint StartPoint, + const OriginID StartOID, + const LoanID TargetLoan) const; + + llvm::SmallVector<OriginFlowChainStep> + buildOriginFlowChainWithFacts(const UseFact *UF, + const LoanID TargetLoan) const; + private: class Impl; std::unique_ptr<Impl> PImpl; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a024e4cebfdcc..63d7b54b5e735 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11069,7 +11069,8 @@ def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global st def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">; def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">; def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">; -def note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the storage of %1" because %2 is marked [[clang::lifetimebound]]">; +def note_lifetime_safety_aliases_storage_lifetimebound : Note< + "%0 aliases the storage of %1 because %2 is marked [[clang::lifetimebound]]">; def warn_lifetime_safety_intra_tu_param_suggestion : Warning<"parameter in intra-TU function should be marked " diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 746ebbfb15c39..335ca28861f4f 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -272,7 +272,8 @@ class LifetimeChecker { // Scope-based expiry (use-after-scope). SemaHelper->reportUseAfterScope( IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc, - getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID))); + getAliasChain( + LoanPropagation.buildOriginFlowChainWithFacts(UF, LID))); } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { @@ -524,13 +525,22 @@ class LifetimeChecker { /// Given a chain of origins that shows how a loan propagates, this function /// extracts the corresponding expressions for each origin. Origins that refer /// to declarations (rather than expressions) are skipped. - llvm::SmallVector<const Expr *> - getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) { - llvm::SmallVector<const Expr *> rs; - for (const OriginID CurrOID : OriginFlowChain) - if (const Expr *CurrExpr = - FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()) - rs.push_back(CurrExpr); + llvm::SmallVector<LifetimeSafetyAliasChainEntry> + getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) { + llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs; + for (const OriginFlowChainStep &Step : OriginFlowChain) { + const Expr *CurrExpr = FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr(); + if (!CurrExpr) + continue; + + LifetimeSafetyAliasChainEntry Entry; + Entry.E = CurrExpr; + if (auto Info = Step.FlowFact->getLifetimeBoundInfo()) { + Entry.LifetimeBoundParam = Info->Param; + Entry.LifetimeBoundImplicitObject = Info->isImplicitObject; + } + rs.push_back(Entry); + } return rs; } }; diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index 3d7fbcdacc830..6c1fb7ba4e214 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -46,6 +46,8 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << "\tSrc: "; OM.dump(getSrcOriginID(), OS); OS << (getKillDest() ? "" : ", Merge"); + if (getLifetimeBoundInfo()) + OS << ", LifetimeBound"; OS << "\n"; } diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 8358c69a5165a..dd6838630947e 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -1085,28 +1085,46 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true); return; } - auto IsArgLifetimeBound = [FD, &Args](unsigned I) -> bool { + auto GetLifetimeBoundInfo = [FD, Call](unsigned I) + -> std::optional<OriginFlowFact::LifetimeBoundInfo> { const ParmVarDecl *PVD = nullptr; if (const auto *Method = dyn_cast<CXXMethodDecl>(FD); Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) { if (I == 0) // For the 'this' argument, the attribute is on the method itself. - return implicitObjectParamIsLifetimeBound(Method) || - shouldTrackImplicitObjectArg( - *Args[0], Method, /*RunningUnderLifetimeSafety=*/true); + return implicitObjectParamIsLifetimeBound(Method) + ? std::optional<OriginFlowFact::LifetimeBoundInfo>( + OriginFlowFact::LifetimeBoundInfo{ + Call, /*Param=*/nullptr, + /*isImplicitObject=*/true}) + : std::nullopt; if ((I - 1) < Method->getNumParams()) // For explicit arguments, find the corresponding parameter // declaration. PVD = Method->getParamDecl(I - 1); + } else if (I < FD->getNumParams()) { + // For free functions or static methods. + PVD = FD->getParamDecl(I); + } + if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>()) + return OriginFlowFact::LifetimeBoundInfo{Call, PVD, + /*isImplicitObject=*/false}; + return std::nullopt; + }; + auto IsArgLifetimeBound = [FD, &Args, &GetLifetimeBoundInfo](unsigned I) { + if (GetLifetimeBoundInfo(I)) + return true; + if (const auto *Method = dyn_cast<CXXMethodDecl>(FD); + Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) { + if (I == 0) + return shouldTrackImplicitObjectArg( + *Args[0], Method, /*RunningUnderLifetimeSafety=*/true); } else if (I == 0 && shouldTrackFirstArgument(FD)) { return true; } else if (I == 1 && shouldTrackSecondArgument(FD)) { return true; - } else if (I < FD->getNumParams()) { - // For free functions or static methods. - PVD = FD->getParamDecl(I); } - return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false; + return false; }; auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool { const auto *Method = dyn_cast<CXXMethodDecl>(FD); @@ -1148,7 +1166,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // inner origins. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( CallList->getOuterOriginID(), ArgList->getOuterOriginID(), - KillSrc)); + KillSrc, GetLifetimeBoundInfo(I))); KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { @@ -1164,7 +1182,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // pointer/reference itself must not outlive the arguments. This // only constrains the top-level origin. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc, + GetLifetimeBoundInfo(I))); KillSrc = false; } } diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp index a67b1b3c0f826..01ef3b67e7351 100644 --- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp @@ -202,14 +202,15 @@ class AnalysisImpl return getLoans(getState(P), OID); } - llvm::SmallVector<OriginID> - buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID, - const LoanID TargetLoan) const { + llvm::SmallVector<OriginFlowChainStep> + buildOriginFlowChainWithFacts(ProgramPoint StartPoint, + const OriginID StartOID, + const LoanID TargetLoan) const { assert(getLoans(StartOID, StartPoint).contains(TargetLoan) && "TargetLoan must be present in the StartOID at the StartPoint"); OriginID CurrOID = StartOID; - llvm::SmallVector<OriginID> OriginFlowChain; + llvm::SmallVector<OriginFlowChainStep> OriginFlowChain; llvm::ArrayRef<const Fact *> Facts = FactMgr.getBlockContaining(StartPoint); const auto *StartIt = llvm::find(Facts, StartPoint); assert(StartIt != Facts.end()); @@ -231,7 +232,7 @@ class AnalysisImpl const OriginID SrcOriginID = OFF->getSrcOriginID(); if (!getLoans(SrcOriginID, OFF).contains(TargetLoan)) continue; - OriginFlowChain.push_back(SrcOriginID); + OriginFlowChain.push_back({SrcOriginID, OFF}); CurrOID = SrcOriginID; } @@ -243,6 +244,28 @@ class AnalysisImpl return {}; } + llvm::SmallVector<OriginID> + buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID, + const LoanID TargetLoan) const { + llvm::SmallVector<OriginID> OriginFlowChain; + for (const OriginFlowChainStep &Step : + buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan)) + OriginFlowChain.push_back(Step.OID); + return OriginFlowChain; + } + + llvm::SmallVector<OriginFlowChainStep> + buildOriginFlowChainWithFacts(const UseFact *UF, + const LoanID TargetLoan) const { + for (const OriginList *Cur = UF->getUsedOrigins(); Cur; + Cur = Cur->peelOuterOrigin()) + if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan)) + return buildOriginFlowChainWithFacts(UF, Cur->getOuterOriginID(), + TargetLoan); + + return {}; + } + llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const { for (const OriginList *Cur = UF->getUsedOrigins(); Cur; @@ -315,4 +338,17 @@ LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const { return PImpl->buildOriginFlowChain(UF, TargetLoan); } + +llvm::SmallVector<OriginFlowChainStep> +LoanPropagationAnalysis::buildOriginFlowChainWithFacts( + ProgramPoint StartPoint, const OriginID StartOID, + const LoanID TargetLoan) const { + return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan); +} + +llvm::SmallVector<OriginFlowChainStep> +LoanPropagationAnalysis::buildOriginFlowChainWithFacts( + const UseFact *UF, const LoanID TargetLoan) const { + return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan); +} } // namespace clang::lifetimes::internal diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index a3d546792fe41..3d8ca9e90abff 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -103,7 +103,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, - llvm::ArrayRef<const Expr *> ExprChain) override { + llvm::ArrayRef<LifetimeSafetyAliasChainEntry> + AliasChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope; @@ -117,7 +118,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here) << DestroyedSubject; - reportAliasingChain(ExprChain); + reportAliasingChain(AliasChain); S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); @@ -655,20 +656,46 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { return CurrExpr->getSourceRange() != LastExpr->getSourceRange(); } - void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) { + void reportAliasingChain( + llvm::ArrayRef<LifetimeSafetyAliasChainEntry> OriginExprChain) { if (OriginExprChain.empty()) return; - const Expr *LastExpr = OriginExprChain.back(); + LifetimeSafetyAliasChainEntry Last = OriginExprChain.back(); + const Expr *LastExpr = Last.E; std::string IssueStr = getDiagSubjectDescription(LastExpr); - for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) { - if (!shouldShowInAliasChain(CurrExpr, LastExpr)) + for (LifetimeSafetyAliasChainEntry Curr : + reverse(OriginExprChain.drop_back())) { + const Expr *CurrExpr = Curr.E; + if (!shouldShowInAliasChain(CurrExpr, LastExpr)) { + if (!Last.LifetimeBoundImplicitObject && !Last.LifetimeBoundParam && + (Curr.LifetimeBoundImplicitObject || Curr.LifetimeBoundParam)) { + Last.LifetimeBoundImplicitObject = Curr.LifetimeBoundImplicitObject; + Last.LifetimeBoundParam = Curr.LifetimeBoundParam; + } continue; - S.Diag(CurrExpr->getBeginLoc(), - diag::note_lifetime_safety_aliases_storage) - << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr) - << IssueStr; + } + if (Last.LifetimeBoundImplicitObject || Last.LifetimeBoundParam) { + std::string LifetimeBoundSubject = "the implicit object parameter"; + if (Last.LifetimeBoundParam) { + LifetimeBoundSubject = "parameter "; + if (Last.LifetimeBoundParam->getIdentifier()) + LifetimeBoundSubject += + "'" + Last.LifetimeBoundParam->getNameAsString() + "'"; + else + LifetimeBoundSubject += "'<unnamed>'"; + } + S.Diag(CurrExpr->getBeginLoc(), + diag::note_lifetime_safety_aliases_storage_lifetimebound) + << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr) + << IssueStr << LifetimeBoundSubject; + } else + S.Diag(CurrExpr->getBeginLoc(), + diag::note_lifetime_safety_aliases_storage) + << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr) + << IssueStr; + Last = Curr; LastExpr = CurrExpr; } } >From 9208df06da6819083e24a7652de1fb0da06ecae1 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Sun, 28 Jun 2026 18:33:51 +0530 Subject: [PATCH 4/6] updated tests to include lifetimebound reason in note --- .../LifetimeSafety/annotation-suggestions.cpp | 12 +- clang/test/Sema/LifetimeSafety/safety-c.c | 2 +- clang/test/Sema/LifetimeSafety/safety.cpp | 109 +++++++++--------- 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index 98c528516b8e7..f551f17d8f5e4 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -278,21 +278,21 @@ View return_view_field(const ViewProvider& v) { // expected-warning {{paramet void test_get_on_temporary_pointer() { const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{temporary object does not live long enough}}. // expected-note@-1 {{temporary object is destroyed here}} - // expected-note@-2 {{result of call to 'get' aliases the storage of temporary object}} + // expected-note@-2 {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} (void)s_ref; // expected-note {{later used here}} } void test_get_on_temporary_ref() { const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{temporary object does not live long enough}}. // expected-note@-1 {{temporary object is destroyed here}} - // expected-note@-2 {{result of call to 'get' aliases the storage of temporary object}} + // expected-note@-2 {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} (void)s_ref; // expected-note {{later used here}} } void test_getView_on_temporary() { View sv = ViewProvider{1}.getView(); // expected-warning {{temporary object does not live long enough}}. // expected-note@-1 {{temporary object is destroyed here}} - // expected-note@-2 {{result of call to 'getView' aliases the storage of temporary object}} + // expected-note@-2 {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} (void)sv; // expected-note {{later used here}} } @@ -603,7 +603,7 @@ void uaf_via_inferred_lifetimebound() { { int local; f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} \ - // expected-note {{result of call to 'return_lambda_capturing_param' aliases the storage of local variable 'local'}} + // expected-note {{result of call to 'return_lambda_capturing_param' aliases the storage of local variable 'local' because parameter 'x' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'local' is destroyed here}} (void)f; // expected-note {{later used here}} } @@ -627,7 +627,7 @@ void test_inference() { { MyObj obj; ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ - // expected-note {{result of call to 'create_target' aliases the storage of local variable 'obj'}} + // expected-note {{result of call to 'create_target' aliases the storage of local variable 'obj' because parameter 'obj' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'obj' is destroyed here}} (void)ptr; // expected-note {{later used here}} } @@ -642,7 +642,7 @@ View* MakeView(const MyObj& in) { // expected-warning {{parameter in intra-TU fu void test_new_allocation() { View* v = MakeView(MyObj{}); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'MakeView' aliases the storage of temporary object}} + // expected-note {{result of call to 'MakeView' aliases the storage of temporary object because parameter 'in' is marked [[clang::lifetimebound]]}} (void)v; // expected-note {{later used here}} } diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c index e9443899c9935..61fc6c8d7dbb3 100644 --- a/clang/test/Sema/LifetimeSafety/safety-c.c +++ b/clang/test/Sema/LifetimeSafety/safety-c.c @@ -63,7 +63,7 @@ void lifetimebound_call(void) { { int i; p = identity(&i); // expected-warning {{local variable 'i' does not live long enough}} \ - // expected-note {{result of call to 'identity' aliases the storage of local variable 'i'}} + // expected-note {{result of call to 'identity' aliases the storage of local variable 'i' because parameter 'p' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'i' is destroyed here}} (void)*p; // expected-note {{later used here}} } diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index 2cbf651eb46b5..95037b634228a 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -697,7 +697,7 @@ void test_lifetimebound_multi_level() { int** pp = &p; int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}} result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}} \ - // expected-note {{result of call to 'return_inner_ptr_addr' aliases the storage of local variable 'pp'}} + // expected-note {{result of call to 'return_inner_ptr_addr' aliases the storage of local variable 'pp' because parameter 'ppp' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'pp' is destroyed here}} (void)**result; // expected-note {{used here}} } @@ -821,7 +821,7 @@ void lifetimebound_simple_function() { { MyObj obj; v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ - // expected-note {{result of call to 'Identity' aliases the storage of local variable 'obj'}} + // expected-note {{result of call to 'Identity' aliases the storage of local variable 'obj' because parameter 'v' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'obj' is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -830,8 +830,8 @@ void lifetimebound_multiple_args_definite() { View v; { MyObj obj1, obj2; - v = Choose(true, // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1'}} \ - // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2'}} + v = Choose(true, // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1' because parameter 'a' is marked [[clang::lifetimebound]]}} \ + // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2' because parameter 'b' is marked [[clang::lifetimebound]]}} obj1, // expected-warning {{local variable 'obj1' does not live long enough}} obj2); // expected-warning {{local variable 'obj2' does not live long enough}} } // expected-note {{local variable 'obj1' is destroyed here}} \ @@ -860,7 +860,7 @@ void lifetimebound_mixed_args() { { MyObj obj1, obj2; v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}} \ - // expected-note {{result of call to 'SelectFirst' aliases the storage of local variable 'obj1'}} + // expected-note {{result of call to 'SelectFirst' aliases the storage of local variable 'obj1' because parameter 'a' is marked [[clang::lifetimebound]]}} obj2); } // expected-note {{local variable 'obj1' is destroyed here}} v.use(); // expected-note {{later used here}} @@ -877,7 +877,7 @@ void lifetimebound_member_function() { { MyObj obj; v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} \ - // expected-note {{result of call to 'getView' aliases the storage of local variable 'obj'}} + // expected-note {{result of call to 'getView' aliases the storage of local variable 'obj' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'obj' is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -902,7 +902,7 @@ void lifetimebound_chained_calls() { { MyObj obj; v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} \ - // expected-note 3 {{result of call to 'Identity' aliases the storage of local variable 'obj'}} + // expected-note 3 {{result of call to 'Identity' aliases the storage of local variable 'obj' because parameter 'v' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'obj' is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -912,7 +912,7 @@ void lifetimebound_with_pointers() { { MyObj obj; ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ - // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'obj'}} + // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'obj' because parameter 'obj' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'obj' is destroyed here}} (void)*ptr; // expected-note {{later used here}} } @@ -922,7 +922,7 @@ void chained_assignment_lifetimebound_call() { { MyObj s; p = Identity(obj = &s); // expected-warning {{does not live long enough}} \ - // expected-note {{result of call to 'Identity' aliases the storage of local variable 's'}} + // expected-note {{result of call to 'Identity' aliases the storage of local variable 's' because parameter 'v' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 's' is destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -955,7 +955,7 @@ void lifetimebound_return_reference() { MyObj obj; View temp_v = obj; // expected-warning {{local variable 'obj' does not live long enough}} const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}} \ - // expected-note {{result of call to 'GetObject' aliases the storage of local variable 'obj'}} + // expected-note {{result of call to 'GetObject' aliases the storage of local variable 'obj' because parameter 'v' is marked [[clang::lifetimebound]]}} ptr = &ref; } // expected-note {{local variable 'obj' is destroyed here}} (void)*ptr; // expected-note {{later used here}} @@ -1010,7 +1010,7 @@ void lifetimebound_make_unique() { { MyObj obj; ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} \ - // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local variable 'obj'}} + // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local variable 'obj' because parameter 'args' is marked [[clang::lifetimebound]]}} } // tu-note {{local variable 'obj' is destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1028,7 +1028,7 @@ void non_lifetimebound_make_unique() { void lifetimebound_make_unique_temp() { std::unique_ptr<LifetimeBoundCtor> ptr = std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{temporary object does not live long enough}} \ // tu-note {{temporary object is destroyed here}} \ - // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj>' aliases the storage of temporary object}} + // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj>' aliases the storage of temporary object because parameter 'args' is marked [[clang::lifetimebound]]}} (void)ptr; // tu-note {{later used here}} } @@ -1066,7 +1066,7 @@ void lifetimebound_make_unique_multi_params() { { MyObj obj_short; ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} \ - // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &>' aliases the storage of local variable 'obj_short'}} + // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked [[clang::lifetimebound]]}} } // tu-note {{local variable 'obj_short' is destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1077,7 +1077,7 @@ void lifetimebound_make_unique_multi_params2() { { MyObj obj_short; ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} \ - // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, int>' aliases the storage of local variable 'obj_short'}} + // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, int>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked [[clang::lifetimebound]]}} } // tu-note {{local variable 'obj_short' is destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1098,7 +1098,7 @@ void lifetimebound_make_unique_multi_params3_1() { { MyObj obj_short; ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \ - // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short'}} + // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked [[clang::lifetimebound]]}} } // tu-note {{local variable 'obj_short' is destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1109,7 +1109,7 @@ void lifetimebound_make_unique_multi_params3_2() { { MyObj obj_short; ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \ - // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short'}} + // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked [[clang::lifetimebound]]}} } // tu-note {{local variable 'obj_short' is destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1393,7 +1393,7 @@ void parentheses(bool cond) { { MyObj a; p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does not live long enough}} \ - // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'a'}} + // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'a' because parameter 'obj' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'a' is destroyed here}} (void)*p; // expected-note {{later used here}} @@ -1425,7 +1425,7 @@ void use_temporary_after_destruction() { void passing_temporary_to_lifetime_bound_function() { View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{temporary object does not live long enough}} \ expected-note {{temporary object is destroyed here}} \ - expected-note {{result of call to 'construct_view' aliases the storage of temporary object}} + expected-note {{result of call to 'construct_view' aliases the storage of temporary object because parameter 'obj' is marked [[clang::lifetimebound]]}} use(a); // expected-note {{later used here}} } @@ -1467,7 +1467,7 @@ int **bit_cast_multilevel() { namespace FullExprCleanupLoc { void var_initializer() { View v = non_trivially_destructed_temporary() // expected-warning {{temporary object does not live long enough}} \ - // expected-note {{result of call to 'getView' aliases the storage of temporary object}} + // expected-note {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} .getView(); // expected-note {{temporary object is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1475,7 +1475,7 @@ void var_initializer() { void expr_statement() { View v; v = non_trivially_destructed_temporary() // expected-warning {{temporary object does not live long enough}} \ - // expected-note {{result of call to 'getView' aliases the storage of temporary object}} + // expected-note {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} .getView(); // expected-note {{temporary object is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1519,7 +1519,7 @@ void foobar() { { StatusOr<MyObj> string_or = getStringOr(); view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} \ - // expected-note {{result of call to 'value' aliases the storage of local variable 'string_or'}} + // expected-note {{result of call to 'value' aliases the storage of local variable 'string_or' because the implicit object parameter is marked [[clang::lifetimebound]]}} value(); } // expected-note {{local variable 'string_or' is destroyed here}} (void)view; // expected-note {{later used here}} @@ -1594,7 +1594,7 @@ void test_user_defined_deref_uaf() { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ - // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} + // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'smart_ptr' is destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1612,7 +1612,7 @@ void test_user_defined_deref_with_view() { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ - // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} + // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'smart_ptr' is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1623,7 +1623,7 @@ void test_user_defined_deref_arrow() { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); p = smart_ptr.operator->(); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ - // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} + // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'smart_ptr' is destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1634,7 +1634,7 @@ void test_user_defined_deref_chained() { MyObj obj; SmartPtr<SmartPtr<MyObj>> double_ptr; p = &(**double_ptr); // expected-warning {{local variable 'double_ptr' does not live long enough}} \ - // expected-note 2 {{expression aliases the storage of local variable 'double_ptr'}} + // expected-note 2 {{expression aliases the storage of local variable 'double_ptr' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'double_ptr' is destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1829,7 +1829,7 @@ void wrong_use_of_move_is_permissive() { MyObj a; p = std::move(a).getData(); // expected-warning {{local variable 'a' does not live long enough}} \ // expected-note {{result of call to 'move<MyObj &>' aliases the storage of local variable 'a'}} \ - // expected-note {{result of call to 'getData' aliases the storage of local variable 'a'}} + // expected-note {{result of call to 'getData' aliases the storage of local variable 'a' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'a' is destroyed here}} (void)p; // expected-note {{later used here}} } @@ -1864,10 +1864,10 @@ void bar() { { S s; x = s.x(); // expected-warning {{local variable 's' does not live long enough}} \ - // expected-note {{result of call to 'x' aliases the storage of local variable 's'}} + // expected-note {{result of call to 'x' aliases the storage of local variable 's' because the implicit object parameter is marked [[clang::lifetimebound]]}} View y = S().x(); // expected-warning {{temporary object does not live long enough}} \ expected-note {{temporary object is destroyed here}} \ - expected-note {{result of call to 'x' aliases the storage of temporary object}} + expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} (void)y; // expected-note {{used here}} } // expected-note {{local variable 's' is destroyed here}} (void)x; // expected-note {{used here}} @@ -1956,19 +1956,19 @@ const S& identity(const S& in [[clang::lifetimebound]]); void test_temporary() { const std::string& x = S().x(); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'x' aliases the storage of temporary object}} + // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} (void)x; // expected-note {{later used here}} const std::string& y = identity(S().x()); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'x' aliases the storage of temporary object}} \ - // expected-note {{result of call to 'identity' aliases the storage of temporary object}} + // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} \ + // expected-note {{result of call to 'identity' aliases the storage of temporary object because parameter 'in' is marked [[clang::lifetimebound]]}} (void)y; // expected-note {{later used here}} std::string_view z; { S s; const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} \ - // expected-note {{result of call to 'x' aliases the storage of local variable 's'}} + // expected-note {{result of call to 'x' aliases the storage of local variable 's' because the implicit object parameter is marked [[clang::lifetimebound]]}} z = zz; // expected-note {{expression aliases the storage of local variable 's'}} } // expected-note {{local variable 's' is destroyed here}} (void)z; // expected-note {{later used here}} @@ -1978,13 +1978,13 @@ void test_lifetime_extension_ok() { const S& x = S(); (void)x; const S& y = identity(S()); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'identity' aliases the storage of temporary object}} + // expected-note {{result of call to 'identity' aliases the storage of temporary object because parameter 'in' is marked [[clang::lifetimebound]]}} (void)y; // expected-note {{later used here}} } const std::string& test_return() { const std::string& x = S().x(); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'x' aliases the storage of temporary object}} + // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked [[clang::lifetimebound]]}} return x; // expected-note {{later used here}} } } // namespace reference_type_decl_ref_expr @@ -2165,11 +2165,11 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { void test() { MemberFuncsTpl<MyObj> mtf; const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{temporary object does not live long enough}} // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'memberA' aliases the storage of temporary object}} + // expected-note {{result of call to 'memberA' aliases the storage of temporary object because parameter 'x' is marked [[clang::lifetimebound]]}} const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{temporary object does not live long enough}} // tu-note {{temporary object is destroyed here}} \ - // tu-note {{result of call to 'memberB' aliases the storage of temporary object}} + // tu-note {{result of call to 'memberB' aliases the storage of temporary object because parameter 'x' is marked [[clang::lifetimebound]]}} const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{temporary object does not live long enough}} // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'memberC' aliases the storage of temporary object}} + // expected-note {{result of call to 'memberC' aliases the storage of temporary object because parameter 'x' is marked [[clang::lifetimebound]]}} (void)pTMA; // expected-note {{later used here}} (void)pTMB; // tu-note {{later used here}} (void)pTMC; // expected-note {{later used here}} @@ -2217,7 +2217,7 @@ void test_optional_arrow_lifetimebound() { std::optional<MyObj> opt; v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}} \ // expected-note {{expression aliases the storage of local variable 'opt'}} \ - // expected-note {{result of call to 'getView' aliases the storage of local variable 'opt'}} + // expected-note {{result of call to 'getView' aliases the storage of local variable 'opt' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'opt' is destroyed here}} v.use(); // expected-note {{later used here}} } @@ -2580,7 +2580,8 @@ struct S { void indexing_with_static_operator() { S()(1, 2); - S& x = S()("1", // expected-note 2 {{expression aliases the storage of temporary object}} + S& x = S()("1", // expected-note {{expression aliases the storage of temporary object because parameter 'a' is marked [[clang::lifetimebound]]}} \ + // expected-note {{expression aliases the storage of temporary object because parameter 'b' is marked [[clang::lifetimebound]]}} 2, // expected-warning {{temporary object does not live long enough}} 3); // expected-warning {{temporary object does not live long enough}} expected-note 2 {{temporary object is destroyed here}} @@ -2606,7 +2607,7 @@ S getS(const std::string &s [[clang::lifetimebound]]); void from_free_function() { S s = getS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'getS' aliases the storage of temporary object}} + // expected-note {{result of call to 'getS' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} use(s); // expected-note {{later used here}} } @@ -2626,14 +2627,14 @@ void from_method() { Factory f; S s = f.make(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'make' aliases the storage of temporary object}} + // expected-note {{result of call to 'make' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} use(s); // expected-note {{later used here}} } void from_static_method() { S s = Factory::create(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'create' aliases the storage of temporary object}} + // expected-note {{result of call to 'create' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} use(s); // expected-note {{later used here}} } @@ -2642,7 +2643,7 @@ void from_lifetimebound_this_method() { { Factory f; value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} \ - // expected-note {{result of call to 'makeThis' aliases the storage of local variable 'f'}} + // expected-note {{result of call to 'makeThis' aliases the storage of local variable 'f' because the implicit object parameter is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'f' is destroyed here}} use(value); // expected-note {{later used here}} } @@ -2652,7 +2653,7 @@ void across_scope() { { std::string str{"abc"}; s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ - // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}} + // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'str' is destroyed here}} use(s); // expected-note {{later used here}} } @@ -2675,7 +2676,7 @@ void assignment_propagation() { { std::string str{"abc"}; a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ - // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}} + // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked [[clang::lifetimebound]]}} b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}} } // expected-note {{local variable 'str' is destroyed here}} use(b); // expected-note {{later used here}} @@ -2686,7 +2687,7 @@ void chained_defaulted_assignment_propagation() { { std::string str{"abc"}; S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ - // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}} + // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked [[clang::lifetimebound]]}} c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}\ // expected-note {{expression aliases the storage of local variable 'str'}} } // expected-note {{local variable 'str' is destroyed here}} @@ -2703,7 +2704,7 @@ void no_annotation() { void mix_annotated_and_not() { S s1 = getS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'getS' aliases the storage of temporary object}} + // expected-note {{result of call to 'getS' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} S s2 = getSNoAnnotation(std::string("temp")); use(s1); // expected-note {{later used here}} use(s2); @@ -2715,7 +2716,7 @@ S multiple_lifetimebound_params() { std::string str{"abc"}; S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} \ // expected-warning {{temporary object does not live long enough}} \ - // expected-note {{result of call to 'getS2' aliases the storage of temporary object}} \ + // expected-note {{result of call to 'getS2' aliases the storage of temporary object because parameter 'b' is marked [[clang::lifetimebound]]}} \ // expected-note {{temporary object is destroyed here}} return s; // expected-note {{returned here}} \ // expected-note {{later used here}} @@ -2736,7 +2737,7 @@ T make(const std::string &s [[clang::lifetimebound]]); void from_template_instantiation() { S s = make<S>(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of temporary object}} + // expected-note {{result of call to 'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} use(s); // expected-note {{later used here}} } @@ -2800,7 +2801,7 @@ SAlias getSAlias(const std::string &s [[clang::lifetimebound]]); void from_typedef_return() { SAlias s = getSAlias(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'getSAlias' aliases the storage of temporary object}} + // expected-note {{result of call to 'getSAlias' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} use(s); // expected-note {{later used here}} } @@ -2875,7 +2876,7 @@ std::unique_ptr<S> getUniqueS(const std::string &s [[clang::lifetimebound]]); void owner_return_unique_ptr_s() { auto ptr = getUniqueS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \ // expected-note {{temporary object is destroyed here}} \ - // expected-note {{result of call to 'getUniqueS' aliases the storage of temporary object}} + // expected-note {{result of call to 'getUniqueS' aliases the storage of temporary object because parameter 's' is marked [[clang::lifetimebound]]}} (void)ptr; // expected-note {{later used here}} } @@ -2892,7 +2893,7 @@ void owner_outlives_lifetimebound_source() { { std::string local; ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} \ - // expected-note {{result of call to 'getUniqueS' aliases the storage of local variable 'local'}} + // expected-note {{result of call to 'getUniqueS' aliases the storage of local variable 'local' because parameter 's' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'local' is destroyed here}} (void)ups; // expected-note {{later used here}} } @@ -2927,7 +2928,7 @@ void nested_local_pointer() { { Bar v; p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} - pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}} + pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v' because parameter 'bar' is marked [[clang::lifetimebound]]}} ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}} } // expected-note {{local variable 'v' is destroyed here}} use(***ppp); // expected-note {{later used here}} @@ -3685,7 +3686,7 @@ void uaf_via_lifetimebound() { { int local; f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} \ - // expected-note {{result of call to 'capture_lifetimebound_param' aliases the storage of local variable 'local'}} + // expected-note {{result of call to 'capture_lifetimebound_param' aliases the storage of local variable 'local' because parameter 'x' is marked [[clang::lifetimebound]]}} } // expected-note {{local variable 'local' is destroyed here}} (void)f; // expected-note {{later used here}} } >From e9cfca0bb9de2a15a78df1cf1048d2909fa290a3 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Sun, 28 Jun 2026 20:11:35 +0530 Subject: [PATCH 5/6] formatted code --- .../Analysis/Analyses/LifetimeSafety/LifetimeSafety.h | 9 ++++----- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 3 ++- clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 9 +++++---- clang/lib/Sema/SemaLifetimeSafety.h | 8 ++++---- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index c177e9e222bdc..2b6c494795739 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -70,11 +70,10 @@ class LifetimeSafetySemaHelper { LifetimeSafetySemaHelper() = default; virtual ~LifetimeSafetySemaHelper() = default; - virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc, - llvm::ArrayRef<LifetimeSafetyAliasChainEntry> - AliasChain) {} + virtual void reportUseAfterScope( + const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, + SourceLocation FreeLoc, + llvm::ArrayRef<LifetimeSafetyAliasChainEntry> AliasChain) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 335ca28861f4f..568fcdab62502 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -529,7 +529,8 @@ class LifetimeChecker { getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) { llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs; for (const OriginFlowChainStep &Step : OriginFlowChain) { - const Expr *CurrExpr = FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr(); + const Expr *CurrExpr = + FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr(); if (!CurrExpr) continue; diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index dd6838630947e..b5b6c92ea7348 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -1085,8 +1085,9 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true); return; } - auto GetLifetimeBoundInfo = [FD, Call](unsigned I) - -> std::optional<OriginFlowFact::LifetimeBoundInfo> { + auto GetLifetimeBoundInfo = + [FD, + Call](unsigned I) -> std::optional<OriginFlowFact::LifetimeBoundInfo> { const ParmVarDecl *PVD = nullptr; if (const auto *Method = dyn_cast<CXXMethodDecl>(FD); Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) { @@ -1165,8 +1166,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // FIXME: Handle origin-shape mismatches gracefully so we can also flow // inner origins. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), - KillSrc, GetLifetimeBoundInfo(I))); + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc, + GetLifetimeBoundInfo(I))); KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 3d8ca9e90abff..1828096debb4c 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -101,10 +101,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} - void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, SourceLocation FreeLoc, - llvm::ArrayRef<LifetimeSafetyAliasChainEntry> - AliasChain) override { + void reportUseAfterScope( + const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, + SourceLocation FreeLoc, + llvm::ArrayRef<LifetimeSafetyAliasChainEntry> AliasChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope; >From aa1a22e6a85f4c312bdf2c168499d221193529d7 Mon Sep 17 00:00:00 2001 From: iitianpushkar <[email protected]> Date: Mon, 29 Jun 2026 17:08:18 +0530 Subject: [PATCH 6/6] improved design by taking lifetimeboundinfo out of originflowfact --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 34 +++++++++++-------- .../Analyses/LifetimeSafety/LifetimeSafety.h | 4 +-- .../Analyses/LifetimeSafety/LoanPropagation.h | 9 ++--- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 11 +++--- clang/lib/Analysis/LifetimeSafety/Facts.cpp | 2 -- .../LifetimeSafety/FactsGenerator.cpp | 29 ++++++++-------- .../LifetimeSafety/LoanPropagation.cpp | 16 ++++----- clang/lib/Sema/SemaLifetimeSafety.h | 15 ++++---- 8 files changed, 56 insertions(+), 64 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index 1e0ef8680f7db..3aad46fb36282 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -20,6 +20,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" @@ -30,6 +31,11 @@ namespace clang::lifetimes::internal { using FactID = utils::ID<struct FactTag>; +struct LifetimeBoundInfo { + const ParmVarDecl *Param = nullptr; + bool IsImplicitObject = false; +}; + /// An abstract base class for a single, atomic lifetime-relevant event. class Fact { @@ -131,37 +137,24 @@ class ExpireFact : public Fact { }; class OriginFlowFact : public Fact { -public: - struct LifetimeBoundInfo { - const Expr *Call = nullptr; - const ParmVarDecl *Param = nullptr; - bool isImplicitObject = false; - }; - -private: OriginID OIDDest; OriginID OIDSrc; // True if the destination origin should be killed (i.e., its current loans // cleared) before the source origin's loans are flowed into it. bool KillDest; - std::optional<LifetimeBoundInfo> LifetimeBound; public: static bool classof(const Fact *F) { return F->getKind() == Kind::OriginFlow; } - OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest, - std::optional<LifetimeBoundInfo> LifetimeBound = std::nullopt) + OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest) : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc), - KillDest(KillDest), LifetimeBound(LifetimeBound) {} + KillDest(KillDest) {} OriginID getDestOriginID() const { return OIDDest; } OriginID getSrcOriginID() const { return OIDSrc; } bool getKillDest() const { return KillDest; } - std::optional<LifetimeBoundInfo> getLifetimeBoundInfo() const { - return LifetimeBound; - } void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override; @@ -398,6 +391,16 @@ class FactManager { const LoanManager &getLoanMgr() const { return LoanMgr; } OriginManager &getOriginMgr() { return OriginMgr; } const OriginManager &getOriginMgr() const { return OriginMgr; } + void setLifetimeBoundInfo(const OriginFlowFact *F, LifetimeBoundInfo Info) { + LifetimeBoundFlows.try_emplace(F, Info); + } + std::optional<LifetimeBoundInfo> + getLifetimeBoundInfo(const OriginFlowFact *F) const { + auto It = LifetimeBoundFlows.find(F); + if (It == LifetimeBoundFlows.end()) + return std::nullopt; + return It->second; + } private: FactID NextFactID{0}; @@ -405,6 +408,7 @@ class FactManager { OriginManager OriginMgr; /// Facts for each CFG block, indexed by block ID. llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts; + llvm::DenseMap<const OriginFlowFact *, LifetimeBoundInfo> LifetimeBoundFlows; llvm::BumpPtrAllocator FactAllocator; }; } // namespace clang::lifetimes::internal diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 2b6c494795739..cad40ead378fd 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -31,6 +31,7 @@ #include "llvm/ADT/PointerUnion.h" #include <cstddef> #include <memory> +#include <optional> namespace clang::lifetimes { @@ -51,8 +52,7 @@ enum class WarningScope { struct LifetimeSafetyAliasChainEntry { const Expr *E = nullptr; - const ParmVarDecl *LifetimeBoundParam = nullptr; - bool LifetimeBoundImplicitObject = false; + std::optional<internal::LifetimeBoundInfo> LifetimeBound; }; /// Abstract interface for operations requiring Sema access. diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h index 92d69f99c8794..4f2b3cedd17ba 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h @@ -29,11 +29,6 @@ namespace clang::lifetimes::internal { using LoanSet = llvm::ImmutableSet<LoanID>; using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>; -struct OriginFlowChainStep { - OriginID OID; - const OriginFlowFact *FlowFact; -}; - class LoanPropagationAnalysis { public: LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, @@ -56,12 +51,12 @@ class LoanPropagationAnalysis { llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const; - llvm::SmallVector<OriginFlowChainStep> + llvm::SmallVector<const OriginFlowFact *> buildOriginFlowChainWithFacts(ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan) const; - llvm::SmallVector<OriginFlowChainStep> + llvm::SmallVector<const OriginFlowFact *> buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan) const; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 568fcdab62502..014bb60fa51b5 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -526,20 +526,17 @@ class LifetimeChecker { /// extracts the corresponding expressions for each origin. Origins that refer /// to declarations (rather than expressions) are skipped. llvm::SmallVector<LifetimeSafetyAliasChainEntry> - getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) { + getAliasChain(llvm::ArrayRef<const OriginFlowFact *> OriginFlowChain) { llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs; - for (const OriginFlowChainStep &Step : OriginFlowChain) { + for (const OriginFlowFact *Flow : OriginFlowChain) { const Expr *CurrExpr = - FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr(); + FactMgr.getOriginMgr().getOrigin(Flow->getSrcOriginID()).getExpr(); if (!CurrExpr) continue; LifetimeSafetyAliasChainEntry Entry; Entry.E = CurrExpr; - if (auto Info = Step.FlowFact->getLifetimeBoundInfo()) { - Entry.LifetimeBoundParam = Info->Param; - Entry.LifetimeBoundImplicitObject = Info->isImplicitObject; - } + Entry.LifetimeBound = FactMgr.getLifetimeBoundInfo(Flow); rs.push_back(Entry); } return rs; diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index 6c1fb7ba4e214..3d7fbcdacc830 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -46,8 +46,6 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << "\tSrc: "; OM.dump(getSrcOriginID(), OS); OS << (getKillDest() ? "" : ", Merge"); - if (getLifetimeBoundInfo()) - OS << ", LifetimeBound"; OS << "\n"; } diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b5b6c92ea7348..e5c071cb75110 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -1086,18 +1086,16 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, return; } auto GetLifetimeBoundInfo = - [FD, - Call](unsigned I) -> std::optional<OriginFlowFact::LifetimeBoundInfo> { + [FD](unsigned I) -> std::optional<LifetimeBoundInfo> { const ParmVarDecl *PVD = nullptr; if (const auto *Method = dyn_cast<CXXMethodDecl>(FD); Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) { if (I == 0) // For the 'this' argument, the attribute is on the method itself. return implicitObjectParamIsLifetimeBound(Method) - ? std::optional<OriginFlowFact::LifetimeBoundInfo>( - OriginFlowFact::LifetimeBoundInfo{ - Call, /*Param=*/nullptr, - /*isImplicitObject=*/true}) + ? std::optional<LifetimeBoundInfo>( + LifetimeBoundInfo{/*Param=*/nullptr, + /*IsImplicitObject=*/true}) : std::nullopt; if ((I - 1) < Method->getNumParams()) // For explicit arguments, find the corresponding parameter @@ -1108,8 +1106,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, PVD = FD->getParamDecl(I); } if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>()) - return OriginFlowFact::LifetimeBoundInfo{Call, PVD, - /*isImplicitObject=*/false}; + return LifetimeBoundInfo{PVD, /*IsImplicitObject=*/false}; return std::nullopt; }; auto IsArgLifetimeBound = [FD, &Args, &GetLifetimeBoundInfo](unsigned I) { @@ -1165,9 +1162,11 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // destination origin lists may have different lengths. // FIXME: Handle origin-shape mismatches gracefully so we can also flow // inner origins. - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc, - GetLifetimeBoundInfo(I))); + auto *F = FactMgr.createFact<OriginFlowFact>( + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc); + if (std::optional<LifetimeBoundInfo> Info = GetLifetimeBoundInfo(I)) + FactMgr.setLifetimeBoundInfo(F, *Info); + CurrentBlockFacts.push_back(F); KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { @@ -1182,9 +1181,11 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // Lifetimebound on a non-GSL-ctor function means the returned // pointer/reference itself must not outlive the arguments. This // only constrains the top-level origin. - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc, - GetLifetimeBoundInfo(I))); + auto *F = FactMgr.createFact<OriginFlowFact>( + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc); + if (std::optional<LifetimeBoundInfo> Info = GetLifetimeBoundInfo(I)) + FactMgr.setLifetimeBoundInfo(F, *Info); + CurrentBlockFacts.push_back(F); KillSrc = false; } } diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp index 01ef3b67e7351..2179ec897cf74 100644 --- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp @@ -202,7 +202,7 @@ class AnalysisImpl return getLoans(getState(P), OID); } - llvm::SmallVector<OriginFlowChainStep> + llvm::SmallVector<const OriginFlowFact *> buildOriginFlowChainWithFacts(ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan) const { @@ -210,7 +210,7 @@ class AnalysisImpl "TargetLoan must be present in the StartOID at the StartPoint"); OriginID CurrOID = StartOID; - llvm::SmallVector<OriginFlowChainStep> OriginFlowChain; + llvm::SmallVector<const OriginFlowFact *> OriginFlowChain; llvm::ArrayRef<const Fact *> Facts = FactMgr.getBlockContaining(StartPoint); const auto *StartIt = llvm::find(Facts, StartPoint); assert(StartIt != Facts.end()); @@ -232,7 +232,7 @@ class AnalysisImpl const OriginID SrcOriginID = OFF->getSrcOriginID(); if (!getLoans(SrcOriginID, OFF).contains(TargetLoan)) continue; - OriginFlowChain.push_back({SrcOriginID, OFF}); + OriginFlowChain.push_back(OFF); CurrOID = SrcOriginID; } @@ -248,13 +248,13 @@ class AnalysisImpl buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan) const { llvm::SmallVector<OriginID> OriginFlowChain; - for (const OriginFlowChainStep &Step : + for (const OriginFlowFact *Flow : buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan)) - OriginFlowChain.push_back(Step.OID); + OriginFlowChain.push_back(Flow->getSrcOriginID()); return OriginFlowChain; } - llvm::SmallVector<OriginFlowChainStep> + llvm::SmallVector<const OriginFlowFact *> buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan) const { for (const OriginList *Cur = UF->getUsedOrigins(); Cur; @@ -339,14 +339,14 @@ LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF, return PImpl->buildOriginFlowChain(UF, TargetLoan); } -llvm::SmallVector<OriginFlowChainStep> +llvm::SmallVector<const OriginFlowFact *> LoanPropagationAnalysis::buildOriginFlowChainWithFacts( ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan) const { return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan); } -llvm::SmallVector<OriginFlowChainStep> +llvm::SmallVector<const OriginFlowFact *> LoanPropagationAnalysis::buildOriginFlowChainWithFacts( const UseFact *UF, const LoanID TargetLoan) const { return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan); diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 1828096debb4c..a2a6913dfe7c8 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -669,20 +669,17 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { reverse(OriginExprChain.drop_back())) { const Expr *CurrExpr = Curr.E; if (!shouldShowInAliasChain(CurrExpr, LastExpr)) { - if (!Last.LifetimeBoundImplicitObject && !Last.LifetimeBoundParam && - (Curr.LifetimeBoundImplicitObject || Curr.LifetimeBoundParam)) { - Last.LifetimeBoundImplicitObject = Curr.LifetimeBoundImplicitObject; - Last.LifetimeBoundParam = Curr.LifetimeBoundParam; - } + if (!Last.LifetimeBound && Curr.LifetimeBound) + Last.LifetimeBound = Curr.LifetimeBound; continue; } - if (Last.LifetimeBoundImplicitObject || Last.LifetimeBoundParam) { + if (Last.LifetimeBound) { std::string LifetimeBoundSubject = "the implicit object parameter"; - if (Last.LifetimeBoundParam) { + if (Last.LifetimeBound->Param) { LifetimeBoundSubject = "parameter "; - if (Last.LifetimeBoundParam->getIdentifier()) + if (Last.LifetimeBound->Param->getIdentifier()) LifetimeBoundSubject += - "'" + Last.LifetimeBoundParam->getNameAsString() + "'"; + "'" + Last.LifetimeBound->Param->getNameAsString() + "'"; else LifetimeBoundSubject += "'<unnamed>'"; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
