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 = dyn_cast<const UseFact *>(F))
77 return UF->getUseExpr()->getExprLoc();
78 if (
const auto *OEF = dyn_cast<const OriginEscapesFact *>(F)) {
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 if (LSOpts.CheckNoescapeViolations)
109 reportNoescapeViolations();
110 if (LSOpts.CheckLifetimeboundViolations)
111 reportLifetimeboundViolations();
112 if (LSOpts.CheckMisplacedLifetimebound)
113 reportMisplacedLifetimebound();
114 if (LSOpts.CheckInapplicableLifetimebound)
115 reportInapplicableLifetimebound();
119 if (AST.getLangOpts().EnableLifetimeSafetyInference)
126 void checkAnnotations(
const OriginEscapesFact *OEF) {
127 OriginID EscapedOID = OEF->getEscapedOriginID();
128 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
129 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
131 if (PVD->hasAttr<NoEscapeAttr>()) {
132 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
133 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
134 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
135 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
136 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
137 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
144 if (PVD->hasAttr<LifetimeBoundAttr>()) {
149 VerifiedLiftimeboundEscapes.insert(PVD);
153 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
154 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
155 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
160 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
166 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
167 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
169 VerifiedLiftimeboundEscapes.insert(MD);
171 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
174 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
175 for (
LoanID LID : EscapedLoans) {
176 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
177 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
180 if (
const auto *PVD = PB->getParmVarDecl())
181 CheckParam(PVD, MovedAtEscape.lookup(LID));
182 else if (
const auto *MD = PB->getImplicitThisParent())
183 CheckImplicitThis(MD);
194 void checkExpiry(
const ExpireFact *EF) {
195 const AccessPath &ExpiredPath = EF->getAccessPath();
196 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
197 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
198 for (
auto &[OID, LiveInfo] : Live) {
199 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
200 for (
LoanID HeldLoanID : HeldLoans) {
201 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
202 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
205 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
206 const Expr *MovedExpr =
nullptr;
207 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
210 if (CurWarning.CausingFactDominatesExpiry)
213 CurWarning.CausingFactDominatesExpiry =
true;
214 CurWarning.CausingFact = LiveInfo.CausingFact;
215 CurWarning.ExpiryLoc = EF->getExpiryLoc();
216 CurWarning.MovedExpr = MovedExpr;
217 CurWarning.InvalidatedByExpr =
nullptr;
229 void checkInvalidation(
const InvalidateOriginFact *IOF) {
230 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
232 LoanSet DirectlyInvalidatedLoans =
233 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
234 auto IsInvalidated = [&](
const Loan *L) {
235 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
236 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
237 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
243 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
244 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
245 for (
auto &[OID, LiveInfo] : Live) {
246 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
247 for (
LoanID LiveLoanID : HeldLoans)
248 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
250 bool LastDomination =
251 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
252 if (!LastDomination) {
253 FinalWarningsMap[LiveLoanID] = {
255 LiveInfo.CausingFact,
257 IOF->getInvalidationExpr(),
264 void issuePendingWarnings() {
265 llvm::TimeTraceScope TimeTrace(
"IssuePendingWarnings");
268 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
269 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
270 const Expr *IssueExpr = L->getIssueExpr();
271 const ParmVarDecl *InvalidatedPVD =
nullptr;
272 if (
const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
273 InvalidatedPVD = PB->getParmVarDecl();
275 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
276 CausingFact =
Warning.CausingFact;
277 const Expr *MovedExpr =
Warning.MovedExpr;
278 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
280 if (
const auto *UF = dyn_cast<const UseFact *>(CausingFact)) {
281 llvm::SmallVector<const Expr *> ExprChain =
282 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
283 if (
Warning.InvalidatedByExpr) {
286 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
289 else if (InvalidatedPVD)
291 SemaHelper->reportUseAfterInvalidation(
292 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr,
297 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
298 MovedExpr, ExpiryLoc, ExprChain);
300 }
else if (
const auto *OEF =
301 dyn_cast<const OriginEscapesFact *>(CausingFact)) {
302 if (
Warning.InvalidatedByExpr) {
303 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
307 SemaHelper->reportInvalidatedField(IssueExpr,
308 FieldEscape->getFieldDecl(),
310 else if (InvalidatedPVD)
312 SemaHelper->reportInvalidatedField(InvalidatedPVD,
313 FieldEscape->getFieldDecl(),
315 }
else if (
const auto *GlobalEscape =
316 dyn_cast<GlobalEscapeFact>(OEF)) {
321 SemaHelper->reportInvalidatedGlobal(IssueExpr,
322 GlobalEscape->getGlobal(),
324 else if (InvalidatedPVD)
326 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
327 GlobalEscape->getGlobal(),
332 llvm_unreachable(
"Unhandled OriginEscapesFact type");
333 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
335 SemaHelper->reportUseAfterReturn(
336 IssueExpr, RetEscape->getReturnExpr(), MovedExpr,
338 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
339 }
else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
341 bool IsCapturedByLambda =
342 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
343 SemaHelper->reportDanglingField(
344 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
345 IsCapturedByLambda, ExpiryLoc);
346 }
else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
349 if (
const auto *
Func = dyn_cast_if_present<FunctionDecl>(FD))
350 IsMain =
Func->isMain();
351 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
352 MovedExpr, ExpiryLoc, IsMain);
354 llvm_unreachable(
"Unhandled OriginEscapesFact type");
356 llvm_unreachable(
"Unhandled CausingFact type");
365 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
366 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
370 assert(FDef->isThisDeclarationADefinition() &&
371 "Expected FunctionDecl to be a definition");
373 const auto &SM = FDef->getASTContext().getSourceManager();
375 auto GetFile = [&SM](
const FunctionDecl *FD) {
376 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
379 const FileID DefFile = GetFile(FDef);
380 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
381 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
382 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
388 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
389 FileID
File = GetFile(FD);
392 for (
auto [SeenFD, _] : Targets)
393 if (GetFile(SeenFD) ==
File)
402 auto redecls = llvm::to_vector(FDef->redecls());
404 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
405 AddCrossTUDecl(Redecl);
410 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
411 EscapingTarget EscapeTarget) {
412 if (llvm::isa<const VarDecl *>(EscapeTarget))
416 const auto *ParmToAnnotate =
417 Decl->getParamDecl(PVD->getFunctionScopeIndex());
418 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
423 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
424 const Expr *EscapeExpr) {
425 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
426 SemaHelper->suggestLifetimeboundToImplicitThis(
431 void suggestAnnotations() {
434 if (!LSOpts.SuggestAnnotations)
436 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
437 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
438 if (
const auto *PVD = dyn_cast<const ParmVarDecl *>(
Target))
439 suggestWithScopeForParmVar(PVD, EscapeTarget);
440 else if (
const auto *MD = dyn_cast<const CXXMethodDecl *>(
Target)) {
441 if (
const auto *EscapeExpr = dyn_cast<const Expr *>(EscapeTarget))
442 suggestWithScopeForImplicitThis(MD, EscapeExpr);
444 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
449 void reportNoescapeViolations() {
450 llvm::TimeTraceScope TimeTrace(
"ReportNoescapeViolations");
451 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
452 if (
const auto *E = dyn_cast<const Expr *>(EscapeTarget))
453 SemaHelper->reportNoescapeViolation(PVD, E);
454 else if (
const auto *FD = dyn_cast<const FieldDecl *>(EscapeTarget))
455 SemaHelper->reportNoescapeViolation(PVD, FD);
456 else if (
const auto *G = dyn_cast<const VarDecl *>(EscapeTarget))
457 SemaHelper->reportNoescapeViolation(PVD, G);
459 llvm_unreachable(
"Unhandled EscapingTarget type");
463 void reportLifetimeboundViolations() {
464 llvm::TimeTraceScope TimeTrace(
"ReportLifetimeboundViolations");
467 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
469 !VerifiedLiftimeboundEscapes.contains(MD))
470 SemaHelper->reportLifetimeboundViolation(MD);
472 if (!PVD->hasAttr<LifetimeBoundAttr>())
474 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
475 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
477 "Implicit lifetimebound parameters "
478 "should escape through return");
479 if (!isImplicit && !Escapes)
480 SemaHelper->reportLifetimeboundViolation(PVD);
486 void reportMisplacedLifetimebound() {
487 llvm::TimeTraceScope TimeTrace(
"ReportMisplacedLifetimebound");
488 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
492 auto TargetDecls = getTargetDeclsForAttr(FDef);
495 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
497 for (
auto [Decl, Scope] : TargetDecls) {
500 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
505 for (
const auto *PDef : FDef->parameters()) {
506 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
507 if (!Attr || Attr->isImplicit())
509 for (
auto [Decl, Scope] : TargetDecls) {
510 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
511 if (!PDecl->hasAttr<LifetimeBoundAttr>())
512 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
517 void reportInapplicableLifetimebound() {
518 llvm::TimeTraceScope TimeTrace(
"ReportInapplicableLifetimebound");
519 const auto *FDef = dyn_cast<FunctionDecl>(FD);
529 for (
const auto &PVD : FDef->parameters())
530 if (PVD->hasAttr<LifetimeBoundAttr>() &&
531 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
533 SemaHelper->reportInapplicableLifetimebound(PVD);
536 void inferAnnotations() {
537 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
538 if (
const auto *MD = dyn_cast<const CXXMethodDecl *>(
Target)) {
541 }
else if (
const auto *PVD = dyn_cast<const ParmVarDecl *>(
Target)) {
542 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
548 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
549 FD->getParamDecl(PVD->getFunctionScopeIndex()));
550 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
551 InferredPVD->addAttr(
552 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
562 llvm::SmallVector<const Expr *>
563 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
564 llvm::SmallVector<const Expr *> rs;
565 for (
const OriginID CurrOID : OriginFlowChain)
566 if (
const Expr *CurrExpr =
567 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
568 rs.push_back(CurrExpr);
580 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
581 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 isOwnerPtrCtor(const CXXConstructorDecl *Ctor, const ParmVarDecl *PVD)
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)