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;
73 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
74 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
75 return UF->getUseExpr()->getExprLoc();
76 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
77 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
78 return ReturnEsc->getReturnExpr()->getExprLoc();
79 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
80 return FieldEsc->getFieldDecl()->getLocation();
82 llvm_unreachable(
"unhandled causing fact in PointerUnion");
86 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
87 const MovedLoansAnalysis &MovedLoans,
88 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
89 AnalysisDeclContext &ADC,
90 LifetimeSafetySemaHelper *SemaHelper)
91 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
92 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
93 AST(ADC.getASTContext()), FD(ADC.getDecl()) {
94 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
95 for (
const Fact *F : FactMgr.getFacts(B))
96 if (
const auto *EF = F->getAs<ExpireFact>())
98 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
99 checkInvalidation(IOF);
100 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
101 checkAnnotations(OEF);
102 issuePendingWarnings();
103 suggestAnnotations();
104 reportNoescapeViolations();
105 reportLifetimeboundViolations();
106 reportMisplacedLifetimebound();
110 if (AST.getLangOpts().EnableLifetimeSafetyInference)
117 void checkAnnotations(
const OriginEscapesFact *OEF) {
118 OriginID EscapedOID = OEF->getEscapedOriginID();
119 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
120 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
122 if (PVD->hasAttr<NoEscapeAttr>()) {
123 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
124 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
125 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
126 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
127 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
128 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
135 if (PVD->hasAttr<LifetimeBoundAttr>()) {
138 VerifiedLiftimeboundEscapes.insert(PVD);
142 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
143 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
144 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
146 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
151 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
152 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
154 VerifiedLiftimeboundEscapes.insert(MD);
156 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
159 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
160 for (
LoanID LID : EscapedLoans) {
161 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
162 const AccessPath &AP = L->getAccessPath();
163 if (
const auto *PVD = AP.getAsPlaceholderParam())
164 CheckParam(PVD, MovedAtEscape.lookup(LID));
165 else if (
const auto *MD = AP.getAsPlaceholderThis())
166 CheckImplicitThis(MD);
176 void checkExpiry(
const ExpireFact *EF) {
177 const AccessPath &ExpiredPath = EF->getAccessPath();
178 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
179 for (
auto &[OID, LiveInfo] : Origins) {
180 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
181 for (
LoanID HeldLoanID : HeldLoans) {
182 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
183 if (ExpiredPath != HeldLoan->getAccessPath())
186 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
187 const Expr *MovedExpr =
nullptr;
188 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
191 if (CurWarning.CausingFactDominatesExpiry)
194 CurWarning.CausingFactDominatesExpiry =
true;
195 CurWarning.CausingFact = LiveInfo.CausingFact;
196 CurWarning.ExpiryLoc = EF->getExpiryLoc();
197 CurWarning.MovedExpr = MovedExpr;
198 CurWarning.InvalidatedByExpr =
nullptr;
208 void checkInvalidation(
const InvalidateOriginFact *IOF) {
209 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
211 LoanSet DirectlyInvalidatedLoans =
212 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
213 auto IsInvalidated = [&](
const Loan *L) {
214 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
215 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
216 if (InvalidL->getAccessPath() == L->getAccessPath())
222 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
223 for (
auto &[OID, LiveInfo] : Origins) {
224 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
225 for (
LoanID LiveLoanID : HeldLoans)
226 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
228 bool LastDomination =
229 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
230 if (!LastDomination) {
231 FinalWarningsMap[LiveLoanID] = {
233 LiveInfo.CausingFact,
235 IOF->getInvalidationExpr(),
242 void issuePendingWarnings() {
245 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
246 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
247 const Expr *IssueExpr = L->getIssuingExpr();
248 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
249 CausingFact =
Warning.CausingFact;
250 const ParmVarDecl *InvalidatedPVD =
251 L->getAccessPath().getAsPlaceholderParam();
252 const Expr *MovedExpr =
Warning.MovedExpr;
253 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
255 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
256 if (
Warning.InvalidatedByExpr) {
259 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
261 else if (InvalidatedPVD)
263 SemaHelper->reportUseAfterInvalidation(
264 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
268 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
269 MovedExpr, ExpiryLoc);
270 }
else if (
const auto *OEF =
271 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
272 if (
Warning.InvalidatedByExpr) {
273 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
277 SemaHelper->reportInvalidatedField(IssueExpr,
278 FieldEscape->getFieldDecl(),
280 else if (InvalidatedPVD)
282 SemaHelper->reportInvalidatedField(InvalidatedPVD,
283 FieldEscape->getFieldDecl(),
285 }
else if (
const auto *GlobalEscape =
286 dyn_cast<GlobalEscapeFact>(OEF)) {
291 SemaHelper->reportInvalidatedGlobal(IssueExpr,
292 GlobalEscape->getGlobal(),
294 else if (InvalidatedPVD)
296 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
297 GlobalEscape->getGlobal(),
302 llvm_unreachable(
"Unhandled OriginEscapesFact type");
303 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
305 SemaHelper->reportUseAfterReturn(
306 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
307 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
309 SemaHelper->reportDanglingField(
310 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
311 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
313 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
314 MovedExpr, ExpiryLoc);
316 llvm_unreachable(
"Unhandled OriginEscapesFact type");
318 llvm_unreachable(
"Unhandled CausingFact type");
327 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
328 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
332 assert(FDef->isThisDeclarationADefinition() &&
333 "Expected FunctionDecl to be a definition");
335 const auto &
SM = FDef->getASTContext().getSourceManager();
337 auto GetFile = [&
SM](
const FunctionDecl *FD) {
338 return SM.getFileID(
SM.getExpansionLoc(FD->getLocation()));
341 const FileID DefFile = GetFile(FDef);
342 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
343 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
344 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
350 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
351 FileID
File = GetFile(FD);
354 for (
auto [SeenFD, _] : Targets)
355 if (GetFile(SeenFD) ==
File)
364 auto redecls = llvm::to_vector(FDef->redecls());
366 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
367 AddCrossTUDecl(Redecl);
372 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
373 EscapingTarget EscapeTarget) {
374 if (llvm::isa<const VarDecl *>(EscapeTarget))
378 const auto *ParmToAnnotate =
379 Decl->getParamDecl(PVD->getFunctionScopeIndex());
380 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
385 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
386 const Expr *EscapeExpr) {
387 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
388 SemaHelper->suggestLifetimeboundToImplicitThis(
393 void suggestAnnotations() {
396 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
397 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
398 suggestWithScopeForParmVar(PVD, EscapeTarget);
399 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
400 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
401 suggestWithScopeForImplicitThis(MD, EscapeExpr);
403 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
408 void reportNoescapeViolations() {
409 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
410 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
411 SemaHelper->reportNoescapeViolation(PVD, E);
412 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
413 SemaHelper->reportNoescapeViolation(PVD, FD);
414 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
415 SemaHelper->reportNoescapeViolation(PVD, G);
417 llvm_unreachable(
"Unhandled EscapingTarget type");
421 void reportLifetimeboundViolations() {
424 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
426 !VerifiedLiftimeboundEscapes.contains(MD))
427 SemaHelper->reportLifetimeboundViolation(MD);
429 if (!PVD->hasAttr<LifetimeBoundAttr>())
431 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
432 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
434 "Implicit lifetimebound parameters "
435 "should escape through return");
436 if (!isImplicit && !Escapes)
437 SemaHelper->reportLifetimeboundViolation(PVD);
443 void reportMisplacedLifetimebound() {
444 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
448 auto TargetDecls = getTargetDeclsForAttr(FDef);
451 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
453 for (
auto [Decl, Scope] : TargetDecls) {
456 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
461 for (
const auto *PDef : FDef->parameters()) {
462 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
463 if (!Attr || Attr->isImplicit())
465 for (
auto [Decl, Scope] : TargetDecls) {
466 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
467 if (!PDecl->hasAttr<LifetimeBoundAttr>())
468 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
473 void inferAnnotations() {
474 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
475 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
478 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
479 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
485 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
486 FD->getParamDecl(PVD->getFunctionScopeIndex()));
487 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
488 InferredPVD->addAttr(
489 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
501 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
502 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper);
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)
llvm::ImmutableMap< OriginID, LivenessInfo > LivenessMap
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const MovedLoansAnalysis &MovedLoans, const LiveOriginsAnalysis &LiveOrigins, FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
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())
U cast(CodeGen::Address addr)