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 const LoanPropagationAnalysis &LoanPropagation;
64 const MovedLoansAnalysis &MovedLoans;
65 const LiveOriginsAnalysis &LiveOrigins;
67 LifetimeSafetySemaHelper *SemaHelper;
72 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
73 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
74 return UF->getUseExpr()->getExprLoc();
75 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
76 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
77 return ReturnEsc->getReturnExpr()->getExprLoc();
78 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
79 return FieldEsc->getFieldDecl()->getLocation();
81 llvm_unreachable(
"unhandled causing fact in PointerUnion");
85 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
86 const MovedLoansAnalysis &MovedLoans,
87 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
88 AnalysisDeclContext &ADC,
89 LifetimeSafetySemaHelper *SemaHelper)
90 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
91 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
92 AST(ADC.getASTContext()), FD(ADC.getDecl()) {
93 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
94 for (
const Fact *F : FactMgr.getFacts(B))
95 if (
const auto *EF = F->getAs<ExpireFact>())
97 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
98 checkInvalidation(IOF);
99 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
100 checkAnnotations(OEF);
101 issuePendingWarnings();
102 suggestAnnotations();
103 reportNoescapeViolations();
107 if (AST.getLangOpts().EnableLifetimeSafetyInference)
114 void checkAnnotations(
const OriginEscapesFact *OEF) {
115 OriginID EscapedOID = OEF->getEscapedOriginID();
116 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
117 auto CheckParam = [&](
const ParmVarDecl *PVD) {
119 if (PVD->hasAttr<NoEscapeAttr>()) {
120 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
121 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
122 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
123 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
124 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
125 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
130 if (!PVD->hasAttr<LifetimeBoundAttr>()) {
131 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
132 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
133 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
135 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
140 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
142 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
143 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
145 for (
LoanID LID : EscapedLoans) {
146 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
147 const AccessPath &AP = L->getAccessPath();
148 if (
const auto *PVD = AP.getAsPlaceholderParam())
150 else if (
const auto *MD = AP.getAsPlaceholderThis())
151 CheckImplicitThis(MD);
161 void checkExpiry(
const ExpireFact *EF) {
162 const AccessPath &ExpiredPath = EF->getAccessPath();
163 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
164 for (
auto &[OID, LiveInfo] : Origins) {
165 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
166 for (
LoanID HeldLoanID : HeldLoans) {
167 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
168 if (ExpiredPath != HeldLoan->getAccessPath())
171 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
172 const Expr *MovedExpr =
nullptr;
173 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
176 if (CurWarning.CausingFactDominatesExpiry)
179 CurWarning.CausingFactDominatesExpiry =
true;
180 CurWarning.CausingFact = LiveInfo.CausingFact;
181 CurWarning.ExpiryLoc = EF->getExpiryLoc();
182 CurWarning.MovedExpr = MovedExpr;
183 CurWarning.InvalidatedByExpr =
nullptr;
193 void checkInvalidation(
const InvalidateOriginFact *IOF) {
194 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
196 LoanSet DirectlyInvalidatedLoans =
197 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
198 auto IsInvalidated = [&](
const Loan *L) {
199 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
200 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
201 if (InvalidL->getAccessPath() == L->getAccessPath())
207 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
208 for (
auto &[OID, LiveInfo] : Origins) {
209 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
210 for (
LoanID LiveLoanID : HeldLoans)
211 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
213 bool LastDomination =
214 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
215 if (!LastDomination) {
216 FinalWarningsMap[LiveLoanID] = {
218 LiveInfo.CausingFact,
220 IOF->getInvalidationExpr(),
227 void issuePendingWarnings() {
230 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
231 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
232 const Expr *IssueExpr = L->getIssuingExpr();
233 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
234 CausingFact =
Warning.CausingFact;
235 const ParmVarDecl *InvalidatedPVD =
236 L->getAccessPath().getAsPlaceholderParam();
237 const Expr *MovedExpr =
Warning.MovedExpr;
238 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
240 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
241 if (
Warning.InvalidatedByExpr) {
244 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
246 else if (InvalidatedPVD)
248 SemaHelper->reportUseAfterInvalidation(
249 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
253 SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
255 }
else if (
const auto *OEF =
256 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
257 if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
259 SemaHelper->reportUseAfterReturn(
260 IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
261 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
263 SemaHelper->reportDanglingField(
264 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
265 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
267 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
268 MovedExpr, ExpiryLoc);
270 llvm_unreachable(
"Unhandled OriginEscapesFact type");
272 llvm_unreachable(
"Unhandled CausingFact type");
278 static const FunctionDecl *getCrossTUDecl(
const FunctionDecl &FD,
280 if (!FD.isExternallyVisible())
282 const FileID DefinitionFile =
SM.getFileID(FD.getLocation());
283 for (
const FunctionDecl *Redecl : FD.redecls())
284 if (
SM.getFileID(Redecl->getLocation()) != DefinitionFile)
290 static const FunctionDecl *getCrossTUDecl(
const ParmVarDecl &PVD,
292 if (
const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
293 return getCrossTUDecl(*FD,
SM);
297 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
298 const ParmVarDecl *PVD,
300 EscapingTarget EscapeTarget) {
301 if (llvm::isa<const VarDecl *>(EscapeTarget))
304 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD,
SM))
305 SemaHelper->suggestLifetimeboundToParmVar(
307 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
315 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
316 const CXXMethodDecl *MD, SourceManager &
SM,
317 const Expr *EscapeExpr) {
318 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD,
SM))
319 SemaHelper->suggestLifetimeboundToImplicitThis(
327 void suggestAnnotations() {
330 SourceManager &
SM = AST.getSourceManager();
331 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
332 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
333 suggestWithScopeForParmVar(SemaHelper, PVD,
SM, EscapeTarget);
334 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
335 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
336 suggestWithScopeForImplicitThis(SemaHelper, MD,
SM, EscapeExpr);
338 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
343 void reportNoescapeViolations() {
344 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
345 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
346 SemaHelper->reportNoescapeViolation(PVD, E);
347 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
348 SemaHelper->reportNoescapeViolation(PVD, FD);
349 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
350 SemaHelper->reportNoescapeViolation(PVD, G);
352 llvm_unreachable(
"Unhandled EscapingTarget type");
356 void inferAnnotations() {
357 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
358 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
361 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
362 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
368 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
369 FD->getParamDecl(PVD->getFunctionScopeIndex()));
370 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
371 InferredPVD->addAttr(
372 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
384 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
385 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...
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
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)