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))
337 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
338 MovedExpr, ExpiryLoc);
340 llvm_unreachable(
"Unhandled OriginEscapesFact type");
342 llvm_unreachable(
"Unhandled CausingFact type");
351 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
352 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
356 assert(FDef->isThisDeclarationADefinition() &&
357 "Expected FunctionDecl to be a definition");
359 const auto &SM = FDef->getASTContext().getSourceManager();
361 auto GetFile = [&SM](
const FunctionDecl *FD) {
362 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
365 const FileID DefFile = GetFile(FDef);
366 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
367 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
368 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
374 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
375 FileID
File = GetFile(FD);
378 for (
auto [SeenFD, _] : Targets)
379 if (GetFile(SeenFD) ==
File)
388 auto redecls = llvm::to_vector(FDef->redecls());
390 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
391 AddCrossTUDecl(Redecl);
396 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
397 EscapingTarget EscapeTarget) {
398 if (llvm::isa<const VarDecl *>(EscapeTarget))
402 const auto *ParmToAnnotate =
403 Decl->getParamDecl(PVD->getFunctionScopeIndex());
404 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
409 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
410 const Expr *EscapeExpr) {
411 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
412 SemaHelper->suggestLifetimeboundToImplicitThis(
417 void suggestAnnotations() {
420 if (!LSOpts.SuggestAnnotations)
422 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
423 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
424 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
425 suggestWithScopeForParmVar(PVD, EscapeTarget);
426 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
427 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
428 suggestWithScopeForImplicitThis(MD, EscapeExpr);
430 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
435 void reportNoescapeViolations() {
436 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
437 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
438 SemaHelper->reportNoescapeViolation(PVD, E);
439 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
440 SemaHelper->reportNoescapeViolation(PVD, FD);
441 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
442 SemaHelper->reportNoescapeViolation(PVD, G);
444 llvm_unreachable(
"Unhandled EscapingTarget type");
448 void reportLifetimeboundViolations() {
451 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
453 !VerifiedLiftimeboundEscapes.contains(MD))
454 SemaHelper->reportLifetimeboundViolation(MD);
456 if (!PVD->hasAttr<LifetimeBoundAttr>())
458 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
459 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
461 "Implicit lifetimebound parameters "
462 "should escape through return");
463 if (!isImplicit && !Escapes)
464 SemaHelper->reportLifetimeboundViolation(PVD);
470 void reportMisplacedLifetimebound() {
471 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
475 auto TargetDecls = getTargetDeclsForAttr(FDef);
478 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
480 for (
auto [Decl, Scope] : TargetDecls) {
483 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
488 for (
const auto *PDef : FDef->parameters()) {
489 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
490 if (!Attr || Attr->isImplicit())
492 for (
auto [Decl, Scope] : TargetDecls) {
493 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
494 if (!PDecl->hasAttr<LifetimeBoundAttr>())
495 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
500 void reportInapplicableLifetimebound() {
501 const auto *FDef = dyn_cast<FunctionDecl>(FD);
511 for (
const auto &PVD : FDef->parameters())
512 if (PVD->hasAttr<LifetimeBoundAttr>() &&
513 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
515 SemaHelper->reportInapplicableLifetimebound(PVD);
518 void inferAnnotations() {
519 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
520 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
523 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
524 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
530 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
531 FD->getParamDecl(PVD->getFunctionScopeIndex()));
532 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
533 InferredPVD->addAttr(
534 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
544 llvm::SmallVector<const Expr *>
545 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
546 llvm::SmallVector<const Expr *> rs;
547 for (
const OriginID CurrOID : OriginFlowChain)
548 if (
const Expr *CurrExpr =
549 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
550 rs.push_back(CurrExpr);
562 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
563 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)