clang 24.0.0git
Checker.cpp
Go to the documentation of this file.
1//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the LifetimeChecker, which detects use-after-free
10// errors by checking if live origins hold loans that have expired.
11//
12//===----------------------------------------------------------------------===//
13
15#include "clang/AST/Decl.h"
16#include "clang/AST/Expr.h"
26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
29
31
33 switch (K) {
35 return true;
38 return false;
39 }
40 llvm_unreachable("unknown liveness kind");
41}
42
43namespace {
44
45/// Struct to store the complete context for a potential lifetime violation.
46struct PendingWarning {
47 SourceLocation ExpiryLoc; // Where the loan expired.
48 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
49 const Expr *MovedExpr;
50 const Expr *InvalidatedByExpr;
51 bool CausingFactDominatesExpiry;
52};
53
54using AnnotationTarget =
55 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
56using EscapingTarget = LifetimeSafetySemaHelper::EscapingTarget;
57
58class LifetimeChecker {
59private:
60 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
61 llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
62 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
63 llvm::DenseMap<const FieldDecl *, const Expr *> FieldOriginEscapesMap;
64 llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
65 const LoanPropagationAnalysis &LoanPropagation;
66 const MovedLoansAnalysis &MovedLoans;
67 const LiveOriginsAnalysis &LiveOrigins;
68 FactManager &FactMgr;
69 LifetimeSafetySemaHelper *SemaHelper;
70 ASTContext &AST;
71 const CFG *Cfg;
72 const Decl *FD;
73 const LifetimeSafetyOpts &LSOpts;
74
75 static SourceLocation
76 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
77 if (const auto *UF = dyn_cast<const UseFact *>(F))
78 return UF->getUseExpr()->getExprLoc();
79 if (const auto *OEF = dyn_cast<const OriginEscapesFact *>(F)) {
80 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
81 return ReturnEsc->getReturnExpr()->getExprLoc();
82 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
83 return FieldEsc->getFieldDecl()->getLocation();
84 }
85 llvm_unreachable("unhandled causing fact in PointerUnion");
86 }
87
88public:
89 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
90 const MovedLoansAnalysis &MovedLoans,
91 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
92 AnalysisDeclContext &ADC,
93 LifetimeSafetySemaHelper *SemaHelper,
94 const LifetimeSafetyOpts &LSOpts)
95 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
96 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
97 AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
98 LSOpts(LSOpts) {
99 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
100 for (const Fact *F : FactMgr.getFacts(B))
101 if (const auto *EF = F->getAs<ExpireFact>())
102 checkExpiry(EF);
103 else if (const auto *IOF = F->getAs<InvalidateOriginFact>())
104 checkInvalidation(IOF);
105 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
106 checkAnnotations(OEF);
107 issuePendingWarnings();
108 suggestAnnotations();
109 if (LSOpts.CheckFieldOriginEscape)
110 reportFieldOriginEscapes();
111 if (LSOpts.CheckNoescapeViolations)
112 reportNoescapeViolations();
113 if (LSOpts.CheckLifetimeboundViolations)
114 reportLifetimeboundViolations();
115 if (LSOpts.CheckMisplacedLifetimebound)
116 reportMisplacedLifetimebound();
117 if (LSOpts.CheckInapplicableLifetimebound)
118 reportInapplicableLifetimebound();
119 // Annotation inference is currently guarded by a frontend flag. In the
120 // future, this might be replaced by a design that differentiates between
121 // explicit and inferred findings with separate warning groups.
122 if (AST.getLangOpts().EnableLifetimeSafetyInference)
123 inferAnnotations();
124 }
125
126 /// Checks if an escaping origin holds a placeholder loan, indicating a
127 /// missing [[clang::lifetimebound]] annotation or a violation of
128 /// [[clang::noescape]].
129 void checkAnnotations(const OriginEscapesFact *OEF) {
130 OriginID EscapedOID = OEF->getEscapedOriginID();
131 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
132 auto CheckParam = [&](const ParmVarDecl *PVD, bool IsMoved) {
133 // NoEscape param should not escape.
134 if (PVD->hasAttr<NoEscapeAttr>()) {
135 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
136 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
137 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
138 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
139 if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
140 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
141 return;
142 }
143 // Skip annotation suggestion for moved loans, as ownership transfer
144 // obscures the lifetime relationship (e.g., shared_ptr from unique_ptr).
145 if (IsMoved)
146 return;
147 if (PVD->hasAttr<LifetimeBoundAttr>()) {
148 // Track that this lifetimebound parameter correctly escapes
149 // (via return or via field assignment in a constructor).
150 if (isa<ReturnEscapeFact>(OEF) ||
152 VerifiedLiftimeboundEscapes.insert(PVD);
153 } else {
154 // Otherwise, suggest lifetimebound for parameter escaping through
155 // return or a field in constructor.
156 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
157 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
158 else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
159 FieldEsc && isa<CXXConstructorDecl>(FD)) {
160 // Disable inference for pointers being captured by an owner type,
161 // as owners typically consume these pointers rather than borrow them.
162 if (!isOwnerPtrCtor(dyn_cast<CXXConstructorDecl>(FD), PVD))
163 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
164 }
165 }
166 // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
167 // field!
168 };
169 auto CheckImplicitThis = [&](const CXXMethodDecl *MD) {
170 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
172 VerifiedLiftimeboundEscapes.insert(MD);
173 else
174 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
175 }
176 };
177 // Identifies methods whose return value derives from a field's origin
178 // rather than *this, e.g., Span::data() or Vector::data().
179 auto CheckField = [&](const FieldDecl *Field) {
180 if (!LSOpts.CheckFieldOriginEscape)
181 return;
182 // Lifetimebound marks the field as owned by *this (e.g., Vector::data()).
183 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
185 return;
186 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
187 // Ignore field loans in inner origins, e.g., returning a reference to
188 // a view field borrows *this.
189 if (OriginList *RetList = FactMgr.getOriginMgr().getOrCreateList(
190 ReturnEsc->getReturnExpr());
191 RetList && RetList->getOuterOriginID() == EscapedOID)
192 FieldOriginEscapesMap.try_emplace(Field, ReturnEsc->getReturnExpr());
193 }
194 };
195 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
196 for (LoanID LID : EscapedLoans) {
197 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
198 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
199 if (!PB)
200 continue;
201 if (const auto *PVD = PB->getParmVarDecl())
202 CheckParam(PVD, /*IsMoved=*/MovedAtEscape.lookup(LID));
203 else if (const auto *MD = PB->getImplicitThisParent())
204 CheckImplicitThis(MD);
205 else if (const auto *Field = PB->getFieldDecl())
206 CheckField(Field);
207 }
208 }
209
210 /// Checks for use-after-free & use-after-return errors when an access path
211 /// expires (e.g., a variable goes out of scope).
212 ///
213 /// When a path expires, all loans prefixed by that path expire. For example,
214 /// if `x` expires, loans to `x`, `x.field`, and `x.field.*` all expire.
215 /// This method examines all live origins and reports warnings for loans they
216 /// hold that are prefixed by the expired path.
217 void checkExpiry(const ExpireFact *EF) {
218 const AccessPath &ExpiredPath = EF->getAccessPath();
219 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
220 for (const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
221 for (auto &[OID, LiveInfo] : Live) {
222 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
223 for (LoanID HeldLoanID : HeldLoans) {
224 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
225 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
226 continue;
227 // HeldLoan is expired because its base or itself is expired.
228 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
229 const Expr *MovedExpr = nullptr;
230 if (auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
231 MovedExpr = *ME;
232 // Skip if we already have a dominating causing fact.
233 if (CurWarning.CausingFactDominatesExpiry)
234 continue;
235 if (causingFactDominatesExpiry(LiveInfo.Kind))
236 CurWarning.CausingFactDominatesExpiry = true;
237 CurWarning.CausingFact = LiveInfo.CausingFact;
238 CurWarning.ExpiryLoc = EF->getExpiryLoc();
239 CurWarning.MovedExpr = MovedExpr;
240 CurWarning.InvalidatedByExpr = nullptr;
241 }
242 }
243 }
244
245 /// Checks for use-after-invalidation errors when a container is modified.
246 ///
247 /// When a container is invalidated, loans pointing into its interior are
248 /// invalidated. For example, if container `v` is invalidated, iterators with
249 /// loans to `v.*` are invalidated. This method finds live origins holding
250 /// such loans and reports warnings. A loan is invalidated if its path extends
251 /// an invalidated container's path (e.g., `v.*` extends `v`).
252 void checkInvalidation(const InvalidateOriginFact *IOF) {
253 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
254 /// Get loans directly pointing to the invalidated container
255 LoanSet DirectlyInvalidatedLoans =
256 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
257 auto IsInvalidated = [&](const Loan *L) {
258 for (LoanID InvalidID : DirectlyInvalidatedLoans) {
259 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
260 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
261 return true;
262 }
263 return false;
264 };
265 // For each live origin, check if it holds an invalidated loan and report.
266 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
267 for (const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
268 for (auto &[OID, LiveInfo] : Live) {
269 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
270 for (LoanID LiveLoanID : HeldLoans)
271 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
272 bool CurDomination = causingFactDominatesExpiry(LiveInfo.Kind);
273 bool LastDomination =
274 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
275 if (!LastDomination) {
276 FinalWarningsMap[LiveLoanID] = {
277 /*ExpiryLoc=*/{},
278 /*CausingFact=*/LiveInfo.CausingFact,
279 /*MovedExpr=*/nullptr,
280 /*InvalidatedByExpr=*/IOF->getInvalidationExpr(),
281 /*CausingFactDominatesExpiry=*/CurDomination};
282 }
283 }
284 }
285 }
286
287 void issuePendingWarnings() {
288 llvm::TimeTraceScope TimeTrace("IssuePendingWarnings");
289 if (!SemaHelper)
290 return;
291 for (const auto &[LID, Warning] : FinalWarningsMap) {
292 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
293 const Expr *IssueExpr = L->getIssueExpr();
294 const ParmVarDecl *InvalidatedPVD = nullptr;
295 if (const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
296 InvalidatedPVD = PB->getParmVarDecl();
297
298 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
299 CausingFact = Warning.CausingFact;
300 const Expr *MovedExpr = Warning.MovedExpr;
301 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
302
303 if (const auto *UF = dyn_cast<const UseFact *>(CausingFact)) {
304 llvm::SmallVector<const Expr *> ExprChain =
305 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
306 if (Warning.InvalidatedByExpr) {
307 if (IssueExpr)
308 // Use-after-invalidation of an object on stack.
309 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
310 Warning.InvalidatedByExpr,
311 ExprChain);
312 else if (InvalidatedPVD)
313 // Use-after-invalidation of a parameter.
314 SemaHelper->reportUseAfterInvalidation(
315 InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr,
316 ExprChain);
317
318 } else
319 // Scope-based expiry (use-after-scope).
320 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
321 MovedExpr, ExpiryLoc, ExprChain);
322
323 } else if (const auto *OEF =
324 dyn_cast<const OriginEscapesFact *>(CausingFact)) {
325 if (Warning.InvalidatedByExpr) {
326 if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
327 // Invalidated object escapes to a field.
328 if (IssueExpr)
329 // Invalidated object on stack escapes to a field.
330 SemaHelper->reportInvalidatedField(IssueExpr,
331 FieldEscape->getFieldDecl(),
332 Warning.InvalidatedByExpr);
333 else if (InvalidatedPVD)
334 // Invalidated parameter escapes to a field.
335 SemaHelper->reportInvalidatedField(InvalidatedPVD,
336 FieldEscape->getFieldDecl(),
337 Warning.InvalidatedByExpr);
338 } else if (const auto *GlobalEscape =
339 dyn_cast<GlobalEscapeFact>(OEF)) {
340 // Invalidated object escapes to global or static storage.
341 if (IssueExpr)
342 // Invalidated object on stack escapes to global or static
343 // storage.
344 SemaHelper->reportInvalidatedGlobal(IssueExpr,
345 GlobalEscape->getGlobal(),
346 Warning.InvalidatedByExpr);
347 else if (InvalidatedPVD)
348 // Invalidated parameter escapes to global or static storage.
349 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
350 GlobalEscape->getGlobal(),
351 Warning.InvalidatedByExpr);
352 } else if (isa<ReturnEscapeFact>(OEF)) {
353 // FIXME: Diagnose invalidated return escapes separately.
354 } else if (const auto *ThisEscape = dyn_cast<ThisEscapeFact>(OEF)) {
355 // A loan still held by the object at the method's exit was
356 // invalidated.
357 llvm::SmallVector<const Expr *> ExprChain = getExprChain(
358 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg));
359 if (IssueExpr)
360 SemaHelper->reportUseAfterInvalidation(
361 IssueExpr, ThisEscape->getLoc(), Warning.InvalidatedByExpr,
362 ExprChain);
363 else if (InvalidatedPVD)
364 SemaHelper->reportUseAfterInvalidation(
365 InvalidatedPVD, ThisEscape->getLoc(),
366 Warning.InvalidatedByExpr, ExprChain);
367 } else
368 llvm_unreachable("Unhandled OriginEscapesFact type");
369 } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
370 // Return stack address.
371 SemaHelper->reportUseAfterReturn(
372 IssueExpr, RetEscape->getReturnExpr(), MovedExpr,
373 getExprChain(
374 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
375 } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
376 // Dangling field.
377 bool IsCapturedByLambda =
378 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
379 SemaHelper->reportDanglingField(
380 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
381 IsCapturedByLambda, ExpiryLoc);
382 } else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
383 // Global escape.
384 bool IsMain = false;
385 if (const auto *Func = dyn_cast_if_present<FunctionDecl>(FD))
386 IsMain = Func->isMain();
387 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
388 MovedExpr, ExpiryLoc, IsMain);
389 } else if (const auto *ThisEscape = dyn_cast<ThisEscapeFact>(OEF))
390 // A borrow is still held by the object (e.g. captured via
391 // lifetime_capture_by_this) when the captured local goes out of
392 // scope; reuse the use-after-scope diagnostic at the capturing
393 // method.
394 SemaHelper->reportUseAfterScope(
395 IssueExpr, ThisEscape->getLoc(), MovedExpr, ExpiryLoc,
396 getExprChain(
397 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
398 else
399 llvm_unreachable("Unhandled OriginEscapesFact type");
400 } else
401 llvm_unreachable("Unhandled CausingFact type");
402 }
403 }
404
405 // Returns declarations that should be annotated with lifetime attributes
406 // in order to annotate FDef: the canonical declaration and the earliest
407 // redeclarations in each other file. This defines the placement policy for
408 // lifetime annotations. Each target is paired with its corresponding warning
409 // scope.
410 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
411 getTargetDeclsForAttr(const FunctionDecl *FDef) {
412 if (!FDef)
413 return {};
414
415 assert(FDef->isThisDeclarationADefinition() &&
416 "Expected FunctionDecl to be a definition");
417
418 const auto &SM = FDef->getASTContext().getSourceManager();
419
420 auto GetFile = [&SM](const FunctionDecl *FD) {
421 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
422 };
423
424 const FileID DefFile = GetFile(FDef);
425 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
426 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
427 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
430
431 // Find the earliest redeclaration in each file other than the definition
432 // file.
433 auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
434 FileID File = GetFile(FD);
435 if (File == DefFile)
436 return;
437 for (auto [SeenFD, _] : Targets)
438 if (GetFile(SeenFD) == File)
439 return;
440 Targets.push_back({FD, WarningScope::CrossTU});
441 };
442
443 // We iterate in reverse order (from most recent to oldest) to find
444 // the first declaration in each file.
445
446 // Store in temporary variable to manually extend lifetime
447 auto redecls = llvm::to_vector(FDef->redecls());
448
449 for (const FunctionDecl *Redecl : llvm::reverse(redecls))
450 AddCrossTUDecl(Redecl);
451
452 return Targets;
453 }
454
455 void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
456 EscapingTarget EscapeTarget) {
457 if (llvm::isa<const VarDecl *>(EscapeTarget))
458 return;
459
460 for (auto [Decl, Scope] : getTargetDeclsForAttr(cast<FunctionDecl>(FD))) {
461 const auto *ParmToAnnotate =
462 Decl->getParamDecl(PVD->getFunctionScopeIndex());
463 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
464 EscapeTarget);
465 }
466 }
467
468 void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
469 const Expr *EscapeExpr) {
470 for (auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
471 SemaHelper->suggestLifetimeboundToImplicitThis(
472 Scope, cast<CXXMethodDecl>(Decl), EscapeExpr);
473 }
474 }
475
476 void suggestAnnotations() {
477 if (!SemaHelper)
478 return;
479 if (!LSOpts.SuggestAnnotations)
480 return;
481 llvm::TimeTraceScope TimeTrace("SuggestAnnotations");
482 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
483 if (const auto *PVD = dyn_cast<const ParmVarDecl *>(Target))
484 suggestWithScopeForParmVar(PVD, EscapeTarget);
485 else if (const auto *MD = dyn_cast<const CXXMethodDecl *>(Target)) {
486 if (const auto *EscapeExpr = dyn_cast<const Expr *>(EscapeTarget))
487 suggestWithScopeForImplicitThis(MD, EscapeExpr);
488 else
489 llvm_unreachable("Implicit this can only escape via Expr (return)");
490 }
491 }
492 }
493
494 void reportFieldOriginEscapes() {
495 if (!SemaHelper)
496 return;
497 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
498 if (!MD)
499 return;
500 llvm::TimeTraceScope TimeTrace("ReportFieldOriginEscapes");
501 for (auto [Field, EscapeExpr] : FieldOriginEscapesMap)
502 SemaHelper->reportFieldOriginEscape(MD, Field, EscapeExpr);
503 }
504
505 void reportNoescapeViolations() {
506 llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");
507 for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
508 if (const auto *E = dyn_cast<const Expr *>(EscapeTarget))
509 SemaHelper->reportNoescapeViolation(PVD, E);
510 else if (const auto *FD = dyn_cast<const FieldDecl *>(EscapeTarget))
511 SemaHelper->reportNoescapeViolation(PVD, FD);
512 else if (const auto *G = dyn_cast<const VarDecl *>(EscapeTarget))
513 SemaHelper->reportNoescapeViolation(PVD, G);
514 else
515 llvm_unreachable("Unhandled EscapingTarget type");
516 }
517 }
518
519 void reportLifetimeboundViolations() {
520 llvm::TimeTraceScope TimeTrace("ReportLifetimeboundViolations");
521 if (!isa<FunctionDecl>(FD))
522 return;
523 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
525 !VerifiedLiftimeboundEscapes.contains(MD))
526 SemaHelper->reportLifetimeboundViolation(MD);
527 for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) {
528 if (!PVD->hasAttr<LifetimeBoundAttr>())
529 continue;
530 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
531 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
532 assert((!isImplicit || Escapes || isInStlNamespace(FD)) &&
533 "Implicit lifetimebound parameters "
534 "should escape through return");
535 if (!isImplicit && !Escapes)
536 SemaHelper->reportLifetimeboundViolation(PVD);
537 }
538 }
539
540 // Reports lifetimebound attributes that are placed on a function definition
541 // but not on the corresponding declaration.
542 void reportMisplacedLifetimebound() {
543 llvm::TimeTraceScope TimeTrace("ReportMisplacedLifetimebound");
544 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
545 if (!FDef)
546 return;
547
548 auto TargetDecls = getTargetDeclsForAttr(FDef);
549 // Check if implicit 'this' has lifetimebound on definition but not on
550 // declaration.
551 if (const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
553 for (auto [Decl, Scope] : TargetDecls) {
554 const auto *MDecl = cast<CXXMethodDecl>(Decl);
556 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
557 }
558
559 // Check each parameter for explicit lifetimebound on definition but not on
560 // declaration.
561 for (const auto *PDef : FDef->parameters()) {
562 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
563 if (!Attr || Attr->isImplicit())
564 continue;
565 for (auto [Decl, Scope] : TargetDecls) {
566 const auto *PDecl = Decl->getParamDecl(PDef->getFunctionScopeIndex());
567 if (!PDecl->hasAttr<LifetimeBoundAttr>())
568 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
569 }
570 }
571 }
572
573 void reportInapplicableLifetimebound() {
574 llvm::TimeTraceScope TimeTrace("ReportInapplicableLifetimebound");
575 const auto *FDef = dyn_cast<FunctionDecl>(FD);
576 if (!FDef)
577 return;
578
579 // If analyzed function is a template definition or an implicit
580 // instantiation, skip.
581 if (FDef->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
582 FDef->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
583 return;
584
585 for (const auto &PVD : FDef->parameters())
586 if (PVD->hasAttr<LifetimeBoundAttr>() &&
587 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
588 /*IntrinsicOnly=*/true))
589 SemaHelper->reportInapplicableLifetimebound(PVD);
590 }
591
592 void inferAnnotations() {
593 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
594 if (const auto *MD = dyn_cast<const CXXMethodDecl *>(Target)) {
596 SemaHelper->addLifetimeBoundToImplicitThis(cast<CXXMethodDecl>(MD));
597 } else if (const auto *PVD = dyn_cast<const ParmVarDecl *>(Target)) {
598 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
599 if (!FD)
600 continue;
601 // Propagates inferred attributes via the most recent declaration to
602 // ensure visibility for callers in post-order analysis.
604 ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
605 FD->getParamDecl(PVD->getFunctionScopeIndex()));
606 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
607 InferredPVD->addAttr(
608 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
609 }
610 }
611 }
612
613 /// Extract expressions from the origin flow chain for diagnostic purposes.
614 ///
615 /// Given a chain of origins that shows how a loan propagates, this function
616 /// extracts the corresponding expressions for each origin. Origins that refer
617 /// to declarations (rather than expressions) are skipped.
618 llvm::SmallVector<const Expr *>
619 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
620 llvm::SmallVector<const Expr *> rs;
621 for (const OriginID CurrOID : OriginFlowChain)
622 if (const Expr *CurrExpr =
623 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
624 rs.push_back(CurrExpr);
625 return rs;
626 }
627};
628} // namespace
629
631 const MovedLoansAnalysis &MovedLoans,
632 const LiveOriginsAnalysis &LO, FactManager &FactMgr,
634 LifetimeSafetySemaHelper *SemaHelper,
635 const LifetimeSafetyOpts &LSOpts) {
636 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
637 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper, LSOpts);
638}
639
640} // namespace clang::lifetimes::internal
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 ...
Definition ASTContext.h:245
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.
Definition CFG.h:1295
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:114
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
Definition Loans.h:27
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
static bool causingFactDominatesExpiry(LivenessKind K)
Definition Checker.cpp:32
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...
Definition Checker.cpp:630
utils::MapTy< OriginID, LivenessInfo > LivenessMap
Definition LiveOrigins.h:76
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)
Definition Address.h:330
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.
Definition Specifiers.h:198
U cast(CodeGen::Address addr)
Definition Address.h:327