https://github.com/AbhinavPradeep created https://github.com/llvm/llvm-project/pull/181646
**Draft PR**. Will fail currently as I have not wired it in. So far: 1. Created `GlobalEscapeFact` as a subclass of `OriginEscapesFact` 2. Emit this fact when, in `FactsGenerator::handleAssignment`, we are assigning to a global variable or a static data member. >From c7565677556963ff4a33028a2b341ef1e5f727e9 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep <[email protected]> Date: Mon, 16 Feb 2026 22:10:24 +1000 Subject: [PATCH] Create and emit GlobalEscapeFact. --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 20 +++++++++++++++++- .../Analyses/LifetimeSafety/LifetimeSafety.h | 4 ++++ clang/lib/Analysis/LifetimeSafety/Checker.cpp | 3 +++ clang/lib/Analysis/LifetimeSafety/Facts.cpp | 8 +++++++ .../LifetimeSafety/FactsGenerator.cpp | 21 +++++++++++++++++++ .../Analysis/LifetimeSafety/LiveOrigins.cpp | 2 ++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index f9d55991f2e09..f797e93fdd85a 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H +#include "clang/AST/Decl.h" #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h" #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" @@ -151,7 +152,7 @@ class OriginEscapesFact : public Fact { enum class EscapeKind : uint8_t { Return, /// Escapes via return statement. Field, /// Escapes via assignment to a field. - // FIXME: Add support for escape to global (dangling global ptr). + Global, /// Escapes via assignment to global storage. } EscKind; static bool classof(const Fact *F) { @@ -202,6 +203,23 @@ class FieldEscapeFact : public OriginEscapesFact { const OriginManager &OM) const override; }; +class GlobalEscapeFact : public OriginEscapesFact { + const VarDecl *VDecl; + +public: + GlobalEscapeFact(OriginID OID, const VarDecl *VDecl) + : OriginEscapesFact(OID, EscapeKind::Global), VDecl(VDecl) {} + + static bool classof(const Fact *F) { + return F->getKind() == Kind::OriginEscapes && + static_cast<const OriginEscapesFact *>(F)->getEscapeKind() == + EscapeKind::Global; + } + const VarDecl *getVarDecl() const { return VDecl; }; + void dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const override; +}; + class UseFact : public Fact { const Expr *UseExpr; const OriginList *OList; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6148f86091110..20b89d5e1f54f 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -95,6 +95,10 @@ class LifetimeSafetySemaHelper { // Reports misuse of [[clang::noescape]] when parameter escapes through field virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const FieldDecl *EscapeField) {} + // Reports misuse of [[clang::noescape]] when parameter escapes through + // assignment to a global variable + virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, + const ValueDecl *EscapeGlobal) {} // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 78c2a6dba3eb6..3daeba353ca2a 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -270,6 +270,9 @@ class LifetimeChecker { else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) SemaHelper->reportDanglingField( IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc); + else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) + // Wire up + ; else llvm_unreachable("Unhandled OriginEscapesFact type"); } else diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index c963d9c45fa9d..fca10310824ea 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -8,6 +8,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclID.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "llvm/ADT/STLFunctionalExtras.h" @@ -68,6 +69,13 @@ void FieldEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << ", via Field)\n"; } +void GlobalEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const { + OS << "OriginEscapes ("; + OM.dump(getEscapedOriginID(), OS); + OS << ", via Global)\n"; +} + void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const { OS << "Use ("; diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b69f69ddbae34..6b0b1a10a6371 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -9,6 +9,7 @@ #include <cassert> #include <string> +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -332,10 +333,17 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) { LHSExpr = LHSExpr->IgnoreParenImpCasts(); OriginList *LHSList = nullptr; + const VarDecl *GlobalLHS = nullptr; if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) { LHSList = getOriginsList(*DRE_LHS); assert(LHSList && "LHS is a DRE and should have an origin list"); + // Check if we are assigning to a global variable + if (const VarDecl *VarD = dyn_cast<VarDecl>(DRE_LHS->getDecl())) { + if (VarD->hasGlobalStorage()) { + GlobalLHS = VarD; + }; + }; } // Handle assignment to member fields (e.g., `this->view = s` or `view = s`). // This enables detection of dangling fields when local values escape to @@ -343,6 +351,12 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr, if (const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) { LHSList = getOriginsList(*ME_LHS); assert(LHSList && "LHS is a MemberExpr and should have an origin list"); + // Check if we are assigning to a static data member + if (const VarDecl *VarD = dyn_cast<VarDecl>(ME_LHS->getMemberDecl())) { + if (VarD->hasGlobalStorage()) { + GlobalLHS = VarD; + }; + }; } if (!LHSList) return; @@ -354,6 +368,13 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr, // assigned. RHSList = getRValueOrigins(RHSExpr, RHSList); + if (GlobalLHS) { + for (OriginList *L = RHSList; L != nullptr; L = L->peelOuterOrigin()) { + EscapesInCurrentBlock.push_back(FactMgr.createFact<GlobalEscapeFact>( + L->getOuterOriginID(), GlobalLHS)); + } + }; + if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) markUseAsWrite(DRE_LHS); // Kill the old loans of the destination origin and flow the new loans diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp index f210fb4d752d4..f818266fcda88 100644 --- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp @@ -62,6 +62,8 @@ static SourceLocation GetFactLoc(CausingFactType F) { return ReturnEsc->getReturnExpr()->getExprLoc(); if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF)) return FieldEsc->getFieldDecl()->getLocation(); + if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF)) + return GlobalEsc->getVarDecl()->getLocation(); } llvm_unreachable("unhandled causing fact in PointerUnion"); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
