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 AccessPath &AP = L->getAccessPath();
170 if (
const auto *PVD = AP.getAsPlaceholderParam())
171 CheckParam(PVD, MovedAtEscape.lookup(LID));
172 else if (
const auto *MD = AP.getAsPlaceholderThis())
173 CheckImplicitThis(MD);
183 void checkExpiry(
const ExpireFact *EF) {
184 const AccessPath &ExpiredPath = EF->getAccessPath();
185 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
186 for (
auto &[OID, LiveInfo] : Origins) {
187 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
188 for (
LoanID HeldLoanID : HeldLoans) {
189 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
190 if (ExpiredPath != HeldLoan->getAccessPath())
193 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
194 const Expr *MovedExpr =
nullptr;
195 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
198 if (CurWarning.CausingFactDominatesExpiry)
201 CurWarning.CausingFactDominatesExpiry =
true;
202 CurWarning.CausingFact = LiveInfo.CausingFact;
203 CurWarning.ExpiryLoc = EF->getExpiryLoc();
204 CurWarning.MovedExpr = MovedExpr;
205 CurWarning.InvalidatedByExpr =
nullptr;
215 void checkInvalidation(
const InvalidateOriginFact *IOF) {
216 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
218 LoanSet DirectlyInvalidatedLoans =
219 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
220 auto IsInvalidated = [&](
const Loan *L) {
221 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
222 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
223 if (InvalidL->getAccessPath() == L->getAccessPath())
229 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
230 for (
auto &[OID, LiveInfo] : Origins) {
231 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
232 for (
LoanID LiveLoanID : HeldLoans)
233 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
235 bool LastDomination =
236 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
237 if (!LastDomination) {
238 FinalWarningsMap[LiveLoanID] = {
240 LiveInfo.CausingFact,
242 IOF->getInvalidationExpr(),
249 void issuePendingWarnings() {
252 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
253 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
254 const Expr *IssueExpr = L->getIssuingExpr();
255 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
256 CausingFact =
Warning.CausingFact;
257 const ParmVarDecl *InvalidatedPVD =
258 L->getAccessPath().getAsPlaceholderParam();
259 const Expr *MovedExpr =
Warning.MovedExpr;
260 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
262 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
263 llvm::SmallVector<const Expr *> ExprChain =
264 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
265 if (
Warning.InvalidatedByExpr) {
268 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
271 else if (InvalidatedPVD)
273 SemaHelper->reportUseAfterInvalidation(
274 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr,
279 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
280 MovedExpr, ExpiryLoc, ExprChain);
282 }
else if (
const auto *OEF =
283 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
284 if (
Warning.InvalidatedByExpr) {
285 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
289 SemaHelper->reportInvalidatedField(IssueExpr,
290 FieldEscape->getFieldDecl(),
292 else if (InvalidatedPVD)
294 SemaHelper->reportInvalidatedField(InvalidatedPVD,
295 FieldEscape->getFieldDecl(),
297 }
else if (
const auto *GlobalEscape =
298 dyn_cast<GlobalEscapeFact>(OEF)) {
303 SemaHelper->reportInvalidatedGlobal(IssueExpr,
304 GlobalEscape->getGlobal(),
306 else if (InvalidatedPVD)
308 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
309 GlobalEscape->getGlobal(),
314 llvm_unreachable(
"Unhandled OriginEscapesFact type");
315 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
317 SemaHelper->reportUseAfterReturn(
318 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
319 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
321 SemaHelper->reportDanglingField(
322 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
323 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
325 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
326 MovedExpr, ExpiryLoc);
328 llvm_unreachable(
"Unhandled OriginEscapesFact type");
330 llvm_unreachable(
"Unhandled CausingFact type");
339 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
340 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
344 assert(FDef->isThisDeclarationADefinition() &&
345 "Expected FunctionDecl to be a definition");
347 const auto &
SM = FDef->getASTContext().getSourceManager();
349 auto GetFile = [&
SM](
const FunctionDecl *FD) {
350 return SM.getFileID(
SM.getExpansionLoc(FD->getLocation()));
353 const FileID DefFile = GetFile(FDef);
354 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
355 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
356 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
362 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
363 FileID
File = GetFile(FD);
366 for (
auto [SeenFD, _] : Targets)
367 if (GetFile(SeenFD) ==
File)
376 auto redecls = llvm::to_vector(FDef->redecls());
378 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
379 AddCrossTUDecl(Redecl);
384 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
385 EscapingTarget EscapeTarget) {
386 if (llvm::isa<const VarDecl *>(EscapeTarget))
390 const auto *ParmToAnnotate =
391 Decl->getParamDecl(PVD->getFunctionScopeIndex());
392 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
397 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
398 const Expr *EscapeExpr) {
399 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
400 SemaHelper->suggestLifetimeboundToImplicitThis(
405 void suggestAnnotations() {
408 if (!LSOpts.SuggestAnnotations)
410 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
411 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
412 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
413 suggestWithScopeForParmVar(PVD, EscapeTarget);
414 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
415 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
416 suggestWithScopeForImplicitThis(MD, EscapeExpr);
418 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
423 void reportNoescapeViolations() {
424 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
425 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
426 SemaHelper->reportNoescapeViolation(PVD, E);
427 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
428 SemaHelper->reportNoescapeViolation(PVD, FD);
429 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
430 SemaHelper->reportNoescapeViolation(PVD, G);
432 llvm_unreachable(
"Unhandled EscapingTarget type");
436 void reportLifetimeboundViolations() {
439 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
441 !VerifiedLiftimeboundEscapes.contains(MD))
442 SemaHelper->reportLifetimeboundViolation(MD);
444 if (!PVD->hasAttr<LifetimeBoundAttr>())
446 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
447 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
449 "Implicit lifetimebound parameters "
450 "should escape through return");
451 if (!isImplicit && !Escapes)
452 SemaHelper->reportLifetimeboundViolation(PVD);
458 void reportMisplacedLifetimebound() {
459 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
463 auto TargetDecls = getTargetDeclsForAttr(FDef);
466 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
468 for (
auto [Decl, Scope] : TargetDecls) {
471 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
476 for (
const auto *PDef : FDef->parameters()) {
477 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
478 if (!Attr || Attr->isImplicit())
480 for (
auto [Decl, Scope] : TargetDecls) {
481 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
482 if (!PDecl->hasAttr<LifetimeBoundAttr>())
483 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
488 void reportInapplicableLifetimebound() {
489 const auto *FDef = dyn_cast<FunctionDecl>(FD);
499 for (
const auto &PVD : FDef->parameters())
500 if (PVD->hasAttr<LifetimeBoundAttr>() &&
501 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
503 SemaHelper->reportInapplicableLifetimebound(PVD);
506 void inferAnnotations() {
507 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
508 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
511 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
512 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
518 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
519 FD->getParamDecl(PVD->getFunctionScopeIndex()));
520 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
521 InferredPVD->addAttr(
522 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
532 llvm::SmallVector<const Expr *>
533 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
534 llvm::SmallVector<const Expr *> rs;
535 for (
const OriginID CurrOID : OriginFlowChain)
536 if (
const Expr *CurrExpr =
537 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
538 rs.push_back(CurrExpr);
550 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
551 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)