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;
71 const LifetimeSafetyOpts &LSOpts;
74 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
75 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
76 return UF->getUseExpr()->getExprLoc();
77 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
78 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
79 return ReturnEsc->getReturnExpr()->getExprLoc();
80 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
81 return FieldEsc->getFieldDecl()->getLocation();
83 llvm_unreachable(
"unhandled causing fact in PointerUnion");
87 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
88 const MovedLoansAnalysis &MovedLoans,
89 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
90 AnalysisDeclContext &ADC,
91 LifetimeSafetySemaHelper *SemaHelper,
92 const LifetimeSafetyOpts &LSOpts)
93 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
94 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
95 AST(ADC.getASTContext()), FD(ADC.getDecl()), LSOpts(LSOpts) {
96 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
97 for (
const Fact *F : FactMgr.getFacts(B))
98 if (
const auto *EF = F->getAs<ExpireFact>())
100 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
101 checkInvalidation(IOF);
102 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
103 checkAnnotations(OEF);
104 issuePendingWarnings();
105 suggestAnnotations();
106 reportNoescapeViolations();
107 reportLifetimeboundViolations();
108 reportMisplacedLifetimebound();
109 reportInapplicableLifetimebound();
113 if (AST.getLangOpts().EnableLifetimeSafetyInference)
120 void checkAnnotations(
const OriginEscapesFact *OEF) {
121 OriginID EscapedOID = OEF->getEscapedOriginID();
122 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
123 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
125 if (PVD->hasAttr<NoEscapeAttr>()) {
126 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
127 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
128 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
129 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
130 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
131 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
138 if (PVD->hasAttr<LifetimeBoundAttr>()) {
143 VerifiedLiftimeboundEscapes.insert(PVD);
147 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
148 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
149 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
151 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
156 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
157 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
159 VerifiedLiftimeboundEscapes.insert(MD);
161 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
164 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
165 for (
LoanID LID : EscapedLoans) {
166 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
167 const AccessPath &AP = L->getAccessPath();
168 if (
const auto *PVD = AP.getAsPlaceholderParam())
169 CheckParam(PVD, MovedAtEscape.lookup(LID));
170 else if (
const auto *MD = AP.getAsPlaceholderThis())
171 CheckImplicitThis(MD);
181 void checkExpiry(
const ExpireFact *EF) {
182 const AccessPath &ExpiredPath = EF->getAccessPath();
183 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
184 for (
auto &[OID, LiveInfo] : Origins) {
185 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
186 for (
LoanID HeldLoanID : HeldLoans) {
187 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
188 if (ExpiredPath != HeldLoan->getAccessPath())
191 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
192 const Expr *MovedExpr =
nullptr;
193 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
196 if (CurWarning.CausingFactDominatesExpiry)
199 CurWarning.CausingFactDominatesExpiry =
true;
200 CurWarning.CausingFact = LiveInfo.CausingFact;
201 CurWarning.ExpiryLoc = EF->getExpiryLoc();
202 CurWarning.MovedExpr = MovedExpr;
203 CurWarning.InvalidatedByExpr =
nullptr;
213 void checkInvalidation(
const InvalidateOriginFact *IOF) {
214 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
216 LoanSet DirectlyInvalidatedLoans =
217 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
218 auto IsInvalidated = [&](
const Loan *L) {
219 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
220 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
221 if (InvalidL->getAccessPath() == L->getAccessPath())
227 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
228 for (
auto &[OID, LiveInfo] : Origins) {
229 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
230 for (
LoanID LiveLoanID : HeldLoans)
231 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
233 bool LastDomination =
234 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
235 if (!LastDomination) {
236 FinalWarningsMap[LiveLoanID] = {
238 LiveInfo.CausingFact,
240 IOF->getInvalidationExpr(),
247 void issuePendingWarnings() {
250 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
251 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
252 const Expr *IssueExpr = L->getIssuingExpr();
253 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
254 CausingFact =
Warning.CausingFact;
255 const ParmVarDecl *InvalidatedPVD =
256 L->getAccessPath().getAsPlaceholderParam();
257 const Expr *MovedExpr =
Warning.MovedExpr;
258 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
260 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
261 if (
Warning.InvalidatedByExpr) {
264 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
266 else if (InvalidatedPVD)
268 SemaHelper->reportUseAfterInvalidation(
269 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
273 SemaHelper->reportUseAfterScope(
274 IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
275 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
277 }
else if (
const auto *OEF =
278 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
279 if (
Warning.InvalidatedByExpr) {
280 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
284 SemaHelper->reportInvalidatedField(IssueExpr,
285 FieldEscape->getFieldDecl(),
287 else if (InvalidatedPVD)
289 SemaHelper->reportInvalidatedField(InvalidatedPVD,
290 FieldEscape->getFieldDecl(),
292 }
else if (
const auto *GlobalEscape =
293 dyn_cast<GlobalEscapeFact>(OEF)) {
298 SemaHelper->reportInvalidatedGlobal(IssueExpr,
299 GlobalEscape->getGlobal(),
301 else if (InvalidatedPVD)
303 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
304 GlobalEscape->getGlobal(),
309 llvm_unreachable(
"Unhandled OriginEscapesFact type");
310 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
312 SemaHelper->reportUseAfterReturn(
313 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
314 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
316 SemaHelper->reportDanglingField(
317 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
318 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
320 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
321 MovedExpr, ExpiryLoc);
323 llvm_unreachable(
"Unhandled OriginEscapesFact type");
325 llvm_unreachable(
"Unhandled CausingFact type");
334 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
335 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
339 assert(FDef->isThisDeclarationADefinition() &&
340 "Expected FunctionDecl to be a definition");
342 const auto &
SM = FDef->getASTContext().getSourceManager();
344 auto GetFile = [&
SM](
const FunctionDecl *FD) {
345 return SM.getFileID(
SM.getExpansionLoc(FD->getLocation()));
348 const FileID DefFile = GetFile(FDef);
349 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
350 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
351 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
357 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
358 FileID
File = GetFile(FD);
361 for (
auto [SeenFD, _] : Targets)
362 if (GetFile(SeenFD) ==
File)
371 auto redecls = llvm::to_vector(FDef->redecls());
373 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
374 AddCrossTUDecl(Redecl);
379 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
380 EscapingTarget EscapeTarget) {
381 if (llvm::isa<const VarDecl *>(EscapeTarget))
385 const auto *ParmToAnnotate =
386 Decl->getParamDecl(PVD->getFunctionScopeIndex());
387 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
392 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
393 const Expr *EscapeExpr) {
394 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
395 SemaHelper->suggestLifetimeboundToImplicitThis(
400 void suggestAnnotations() {
403 if (!LSOpts.SuggestAnnotations)
405 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
406 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
407 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
408 suggestWithScopeForParmVar(PVD, EscapeTarget);
409 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
410 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
411 suggestWithScopeForImplicitThis(MD, EscapeExpr);
413 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
418 void reportNoescapeViolations() {
419 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
420 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
421 SemaHelper->reportNoescapeViolation(PVD, E);
422 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
423 SemaHelper->reportNoescapeViolation(PVD, FD);
424 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
425 SemaHelper->reportNoescapeViolation(PVD, G);
427 llvm_unreachable(
"Unhandled EscapingTarget type");
431 void reportLifetimeboundViolations() {
434 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
436 !VerifiedLiftimeboundEscapes.contains(MD))
437 SemaHelper->reportLifetimeboundViolation(MD);
439 if (!PVD->hasAttr<LifetimeBoundAttr>())
441 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
442 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
444 "Implicit lifetimebound parameters "
445 "should escape through return");
446 if (!isImplicit && !Escapes)
447 SemaHelper->reportLifetimeboundViolation(PVD);
453 void reportMisplacedLifetimebound() {
454 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
458 auto TargetDecls = getTargetDeclsForAttr(FDef);
461 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
463 for (
auto [Decl, Scope] : TargetDecls) {
466 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
471 for (
const auto *PDef : FDef->parameters()) {
472 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
473 if (!Attr || Attr->isImplicit())
475 for (
auto [Decl, Scope] : TargetDecls) {
476 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
477 if (!PDecl->hasAttr<LifetimeBoundAttr>())
478 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
483 void reportInapplicableLifetimebound() {
484 const auto *FDef = dyn_cast<FunctionDecl>(FD);
494 for (
const auto &PVD : FDef->parameters())
495 if (PVD->hasAttr<LifetimeBoundAttr>() &&
496 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
498 SemaHelper->reportInapplicableLifetimebound(PVD);
501 void inferAnnotations() {
502 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
503 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
506 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
507 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
513 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
514 FD->getParamDecl(PVD->getFunctionScopeIndex()));
515 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
516 InferredPVD->addAttr(
517 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
527 llvm::SmallVector<const Expr *>
528 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
529 llvm::SmallVector<const Expr *> rs;
530 for (
const OriginID CurrOID : OriginFlowChain)
531 if (
const Expr *CurrExpr =
532 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
533 rs.push_back(CurrExpr);
545 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
546 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.
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
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
static bool causingFactDominatesExpiry(LivenessKind K)
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...
llvm::ImmutableMap< 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)