26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
40 llvm_unreachable(
"unknown liveness kind");
46struct PendingWarning {
48 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
49 const Expr *MovedExpr;
50 const Expr *InvalidatedByExpr;
51 bool CausingFactDominatesExpiry;
54using AnnotationTarget =
55 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
58class LifetimeChecker {
60 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
61 llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
62 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
63 llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
64 const LoanPropagationAnalysis &LoanPropagation;
65 const MovedLoansAnalysis &MovedLoans;
66 const LiveOriginsAnalysis &LiveOrigins;
68 LifetimeSafetySemaHelper *SemaHelper;
72 const LifetimeSafetyOpts &LSOpts;
75 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
76 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
77 return UF->getUseExpr()->getExprLoc();
78 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
79 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
80 return ReturnEsc->getReturnExpr()->getExprLoc();
81 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
82 return FieldEsc->getFieldDecl()->getLocation();
84 llvm_unreachable(
"unhandled causing fact in PointerUnion");
88 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
89 const MovedLoansAnalysis &MovedLoans,
90 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
91 AnalysisDeclContext &ADC,
92 LifetimeSafetySemaHelper *SemaHelper,
93 const LifetimeSafetyOpts &LSOpts)
94 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
95 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
96 AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
98 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
99 for (
const Fact *F : FactMgr.getFacts(B))
100 if (
const auto *EF = F->getAs<ExpireFact>())
102 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
103 checkInvalidation(IOF);
104 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
105 checkAnnotations(OEF);
106 issuePendingWarnings();
107 suggestAnnotations();
108 reportNoescapeViolations();
109 reportLifetimeboundViolations();
110 reportMisplacedLifetimebound();
111 reportInapplicableLifetimebound();
115 if (AST.getLangOpts().EnableLifetimeSafetyInference)
122 void checkAnnotations(
const OriginEscapesFact *OEF) {
123 OriginID EscapedOID = OEF->getEscapedOriginID();
124 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
125 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
127 if (PVD->hasAttr<NoEscapeAttr>()) {
128 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
129 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
130 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
131 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
132 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
133 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
140 if (PVD->hasAttr<LifetimeBoundAttr>()) {
145 VerifiedLiftimeboundEscapes.insert(PVD);
149 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
150 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
151 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
153 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
158 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
159 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
161 VerifiedLiftimeboundEscapes.insert(MD);
163 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
166 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
167 for (
LoanID LID : EscapedLoans) {
168 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
169 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
172 if (
const auto *PVD = PB->getParmVarDecl())
173 CheckParam(PVD, MovedAtEscape.lookup(LID));
174 else if (
const auto *MD = PB->getImplicitThisParent())
175 CheckImplicitThis(MD);
186 void checkExpiry(
const ExpireFact *EF) {
187 const AccessPath &ExpiredPath = EF->getAccessPath();
188 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
189 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
190 for (
auto &[OID, LiveInfo] : Live) {
191 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
192 for (
LoanID HeldLoanID : HeldLoans) {
193 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
194 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
197 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
198 const Expr *MovedExpr =
nullptr;
199 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
202 if (CurWarning.CausingFactDominatesExpiry)
205 CurWarning.CausingFactDominatesExpiry =
true;
206 CurWarning.CausingFact = LiveInfo.CausingFact;
207 CurWarning.ExpiryLoc = EF->getExpiryLoc();
208 CurWarning.MovedExpr = MovedExpr;
209 CurWarning.InvalidatedByExpr =
nullptr;
221 void checkInvalidation(
const InvalidateOriginFact *IOF) {
222 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
224 LoanSet DirectlyInvalidatedLoans =
225 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
226 auto IsInvalidated = [&](
const Loan *L) {
227 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
228 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
229 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
235 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
236 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
237 for (
auto &[OID, LiveInfo] : Live) {
238 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
239 for (
LoanID LiveLoanID : HeldLoans)
240 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
242 bool LastDomination =
243 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
244 if (!LastDomination) {
245 FinalWarningsMap[LiveLoanID] = {
247 LiveInfo.CausingFact,
249 IOF->getInvalidationExpr(),
256 void issuePendingWarnings() {
259 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
260 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
261 const Expr *IssueExpr = L->getIssueExpr();
262 const ParmVarDecl *InvalidatedPVD =
nullptr;
263 if (
const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
264 InvalidatedPVD = PB->getParmVarDecl();
266 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
267 CausingFact =
Warning.CausingFact;
268 const Expr *MovedExpr =
Warning.MovedExpr;
269 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
271 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
272 llvm::SmallVector<const Expr *> ExprChain =
273 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
274 if (
Warning.InvalidatedByExpr) {
277 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
280 else if (InvalidatedPVD)
282 SemaHelper->reportUseAfterInvalidation(
283 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr,
288 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
289 MovedExpr, ExpiryLoc, ExprChain);
291 }
else if (
const auto *OEF =
292 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
293 if (
Warning.InvalidatedByExpr) {
294 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
298 SemaHelper->reportInvalidatedField(IssueExpr,
299 FieldEscape->getFieldDecl(),
301 else if (InvalidatedPVD)
303 SemaHelper->reportInvalidatedField(InvalidatedPVD,
304 FieldEscape->getFieldDecl(),
306 }
else if (
const auto *GlobalEscape =
307 dyn_cast<GlobalEscapeFact>(OEF)) {
312 SemaHelper->reportInvalidatedGlobal(IssueExpr,
313 GlobalEscape->getGlobal(),
315 else if (InvalidatedPVD)
317 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
318 GlobalEscape->getGlobal(),
323 llvm_unreachable(
"Unhandled OriginEscapesFact type");
324 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
326 SemaHelper->reportUseAfterReturn(
327 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
328 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
330 bool IsCapturedByLambda =
331 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
332 SemaHelper->reportDanglingField(
333 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
334 IsCapturedByLambda, ExpiryLoc);
335 }
else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
338 if (
const auto *
Func = dyn_cast_if_present<FunctionDecl>(FD))
339 IsMain =
Func->isMain();
340 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
341 MovedExpr, ExpiryLoc, IsMain);
343 llvm_unreachable(
"Unhandled OriginEscapesFact type");
345 llvm_unreachable(
"Unhandled CausingFact type");
354 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
355 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
359 assert(FDef->isThisDeclarationADefinition() &&
360 "Expected FunctionDecl to be a definition");
362 const auto &SM = FDef->getASTContext().getSourceManager();
364 auto GetFile = [&SM](
const FunctionDecl *FD) {
365 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
368 const FileID DefFile = GetFile(FDef);
369 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
370 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
371 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
377 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
378 FileID
File = GetFile(FD);
381 for (
auto [SeenFD, _] : Targets)
382 if (GetFile(SeenFD) ==
File)
391 auto redecls = llvm::to_vector(FDef->redecls());
393 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
394 AddCrossTUDecl(Redecl);
399 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
400 EscapingTarget EscapeTarget) {
401 if (llvm::isa<const VarDecl *>(EscapeTarget))
405 const auto *ParmToAnnotate =
406 Decl->getParamDecl(PVD->getFunctionScopeIndex());
407 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
412 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
413 const Expr *EscapeExpr) {
414 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
415 SemaHelper->suggestLifetimeboundToImplicitThis(
420 void suggestAnnotations() {
423 if (!LSOpts.SuggestAnnotations)
425 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
426 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
427 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
428 suggestWithScopeForParmVar(PVD, EscapeTarget);
429 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
430 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
431 suggestWithScopeForImplicitThis(MD, EscapeExpr);
433 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
438 void reportNoescapeViolations() {
439 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
440 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
441 SemaHelper->reportNoescapeViolation(PVD, E);
442 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
443 SemaHelper->reportNoescapeViolation(PVD, FD);
444 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
445 SemaHelper->reportNoescapeViolation(PVD, G);
447 llvm_unreachable(
"Unhandled EscapingTarget type");
451 void reportLifetimeboundViolations() {
454 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
456 !VerifiedLiftimeboundEscapes.contains(MD))
457 SemaHelper->reportLifetimeboundViolation(MD);
459 if (!PVD->hasAttr<LifetimeBoundAttr>())
461 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
462 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
464 "Implicit lifetimebound parameters "
465 "should escape through return");
466 if (!isImplicit && !Escapes)
467 SemaHelper->reportLifetimeboundViolation(PVD);
473 void reportMisplacedLifetimebound() {
474 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
478 auto TargetDecls = getTargetDeclsForAttr(FDef);
481 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
483 for (
auto [Decl, Scope] : TargetDecls) {
486 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
491 for (
const auto *PDef : FDef->parameters()) {
492 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
493 if (!Attr || Attr->isImplicit())
495 for (
auto [Decl, Scope] : TargetDecls) {
496 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
497 if (!PDecl->hasAttr<LifetimeBoundAttr>())
498 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
503 void reportInapplicableLifetimebound() {
504 const auto *FDef = dyn_cast<FunctionDecl>(FD);
514 for (
const auto &PVD : FDef->parameters())
515 if (PVD->hasAttr<LifetimeBoundAttr>() &&
516 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
518 SemaHelper->reportInapplicableLifetimebound(PVD);
521 void inferAnnotations() {
522 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
523 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
526 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
527 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
533 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
534 FD->getParamDecl(PVD->getFunctionScopeIndex()));
535 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
536 InferredPVD->addAttr(
537 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
547 llvm::SmallVector<const Expr *>
548 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
549 llvm::SmallVector<const Expr *> rs;
550 for (
const OriginID CurrOID : OriginFlowChain)
551 if (
const Expr *CurrExpr =
552 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
553 rs.push_back(CurrExpr);
565 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
566 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper, LSOpts);
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Decl - This represents one declaration (or definition), e.g.
This represents one expression.
Encodes a location in the source.
Abstract interface for operations requiring Sema access.
llvm::PointerUnion< const Expr *, const FieldDecl *, const VarDecl * > EscapingTarget
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
static bool causingFactDominatesExpiry(LivenessKind K)
utils::SetTy< LoanID > LoanSet
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const MovedLoansAnalysis &MovedLoans, const LiveOriginsAnalysis &LiveOrigins, FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper, const LifetimeSafetyOpts &LSOpts)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
utils::MapTy< OriginID, LivenessInfo > LivenessMap
const LifetimeBoundAttr * getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInStlNamespace(const Decl *D)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
bool isa(CodeGen::Address addr)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
@ TSK_ImplicitInstantiation
This template specialization was implicitly instantiated from a template.
U cast(CodeGen::Address addr)