1//===- FactsGenerator.cpp - Lifetime Facts Generation -----------*- 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#include <cassert>
10#include <string>
11
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/Expr.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/OperationKinds.h"
17#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
18#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
19#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
20#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
21#include "clang/Analysis/Analyses/PostOrderCFGView.h"
22#include "clang/Analysis/CFG.h"
23#include "llvm/ADT/ArrayRef.h"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/Support/Casting.h"
26#include "llvm/Support/Signals.h"
27#include "llvm/Support/TimeProfiler.h"
28
29namespace clang::lifetimes::internal {
30using llvm::isa_and_present;
31
32OriginList *FactsGenerator::getOriginsList(const ValueDecl &D) {
33 return FactMgr.getOriginMgr().getOrCreateList(D: &D);
34}
35OriginList *FactsGenerator::getOriginsList(const Expr &E) {
36 return FactMgr.getOriginMgr().getOrCreateList(E: &E);
37}
38
39/// Propagates origin information from Src to Dst through all levels of
40/// indirection, creating OriginFlowFacts at each level.
41///
42/// This function enforces a critical type-safety invariant: both lists must
43/// have the same shape (same depth/structure). This invariant ensures that
44/// origins flow only between compatible types during expression evaluation.
45///
46/// Examples:
47/// - `int* p = &x;` flows origins from `&x` (depth 1) to `p` (depth 1)
48/// - `int** pp = &p;` flows origins from `&p` (depth 2) to `pp` (depth 2)
49/// * Level 1: pp <- p's address
50/// * Level 2: (*pp) <- what p points to (i.e., &x)
51/// - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
52void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) {
53 if (!Dst)
54 return;
55 assert(Src &&
56 "Dst is non-null but Src is null. List must have the same length");
57 assert(Dst->getLength() == Src->getLength() &&
58 "Lists must have the same length");
59
60 while (Dst && Src) {
61 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
62 args: Dst->getOuterOriginID(), args: Src->getOuterOriginID(), args&: Kill));
63 Dst = Dst->peelOuterOrigin();
64 Src = Src->peelOuterOrigin();
65 }
66}
67
68/// Creates a loan for the storage path of a given declaration reference.
69/// This function should be called whenever a DeclRefExpr represents a borrow.
70/// \param DRE The declaration reference expression that initiates the borrow.
71/// \return The new Loan on success, nullptr otherwise.
72static const PathLoan *createLoan(FactManager &FactMgr,
73 const DeclRefExpr *DRE) {
74 if (const auto *VD = dyn_cast<ValueDecl>(Val: DRE->getDecl())) {
75 AccessPath Path(VD);
76 // The loan is created at the location of the DeclRefExpr.
77 return FactMgr.getLoanMgr().createLoan<PathLoan>(args&: Path, args&: DRE);
78 }
79 return nullptr;
80}
81
82/// Creates a loan for the storage location of a temporary object.
83/// \param MTE The MaterializeTemporaryExpr that represents the temporary
84/// binding. \return The new Loan.
85static const PathLoan *createLoan(FactManager &FactMgr,
86 const MaterializeTemporaryExpr *MTE) {
87 AccessPath Path(MTE);
88 return FactMgr.getLoanMgr().createLoan<PathLoan>(args&: Path, args&: MTE);
89}
90
91void FactsGenerator::run() {
92 llvm::TimeTraceScope TimeProfile("FactGenerator");
93 const CFG &Cfg = *AC.getCFG();
94 llvm::SmallVector<Fact *> PlaceholderLoanFacts = issuePlaceholderLoans();
95 // Iterate through the CFG blocks in reverse post-order to ensure that
96 // initializations and destructions are processed in the correct sequence.
97 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
98 CurrentBlockFacts.clear();
99 EscapesInCurrentBlock.clear();
100 if (Block == &Cfg.getEntry())
101 CurrentBlockFacts.append(in_start: PlaceholderLoanFacts.begin(),
102 in_end: PlaceholderLoanFacts.end());
103 for (unsigned I = 0; I < Block->size(); ++I) {
104 const CFGElement &Element = Block->Elements[I];
105 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
106 Visit(S: CS->getStmt());
107 else if (std::optional<CFGInitializer> Initializer =
108 Element.getAs<CFGInitializer>())
109 handleCXXCtorInitializer(CII: Initializer->getInitializer());
110 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
111 Element.getAs<CFGLifetimeEnds>())
112 handleLifetimeEnds(LifetimeEnds: *LifetimeEnds);
113 else if (std::optional<CFGFullExprCleanup> FullExprCleanup =
114 Element.getAs<CFGFullExprCleanup>()) {
115 handleFullExprCleanup(FullExprCleanup: *FullExprCleanup);
116 }
117 }
118 if (Block == &Cfg.getExit())
119 handleExitBlock();
120
121 CurrentBlockFacts.append(in_start: EscapesInCurrentBlock.begin(),
122 in_end: EscapesInCurrentBlock.end());
123 FactMgr.addBlockFacts(B: Block, NewFacts: CurrentBlockFacts);
124 }
125}
126
127/// Simulates LValueToRValue conversion by peeling the outer lvalue origin
128/// if the expression is a GLValue. For pointer/view GLValues, this strips
129/// the origin representing the storage location to get the origins of the
130/// pointed-to value.
131///
132/// Example: For `View& v`, returns the origin of what v points to, not v's
133/// storage.
134static OriginList *getRValueOrigins(const Expr *E, OriginList *List) {
135 if (!List)
136 return nullptr;
137 return E->isGLValue() ? List->peelOuterOrigin() : List;
138}
139
140void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
141 for (const Decl *D : DS->decls())
142 if (const auto *VD = dyn_cast<VarDecl>(Val: D))
143 if (const Expr *InitExpr = VD->getInit()) {
144 OriginList *VDList = getOriginsList(D: *VD);
145 if (!VDList)
146 continue;
147 OriginList *InitList = getOriginsList(E: *InitExpr);
148 assert(InitList && "VarDecl had origins but InitExpr did not");
149 flow(Dst: VDList, Src: InitList, /*Kill=*/true);
150 }
151}
152
153void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
154 // Skip function references as their lifetimes are not interesting. Skip non
155 // GLValues (like EnumConstants).
156 if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())
157 return;
158 handleUse(E: DRE);
159 // For all declarations with storage (non-references), we issue a loan
160 // representing the borrow of the variable's storage itself.
161 //
162 // Examples:
163 // - `int x; x` issues loan to x's storage
164 // - `int* p; p` issues loan to p's storage (the pointer variable)
165 // - `View v; v` issues loan to v's storage (the view object)
166 // - `int& r = x; r` issues no loan (r has no storage, it's an alias to x)
167 if (doesDeclHaveStorage(D: DRE->getDecl())) {
168 const Loan *L = createLoan(FactMgr, DRE);
169 assert(L);
170 OriginList *List = getOriginsList(E: *DRE);
171 assert(List &&
172 "gl-value DRE of non-pointer type should have an origin list");
173 // This loan specifically tracks borrowing the variable's storage location
174 // itself and is issued to outermost origin (List->OID).
175 CurrentBlockFacts.push_back(
176 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
177 }
178}
179
180void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
181 if (isGslPointerType(QT: CCE->getType())) {
182 handleGSLPointerConstruction(CCE);
183 return;
184 }
185 // Implicit copy/move constructors of lambda closures lack
186 // [[clang::lifetimebound]], so `handleFunctionCall` cannot propagate origins.
187 // Handle them directly to keep the origin chain intact (e.g., `return
188 // lambda;` copies the closure).
189 if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
190 RD && RD->isLambda() &&
191 CCE->getConstructor()->isCopyOrMoveConstructor() &&
192 CCE->getNumArgs() == 1) {
193 const Expr *Arg = CCE->getArg(Arg: 0);
194 if (OriginList *ArgList = getRValueOrigins(E: Arg, List: getOriginsList(E: *Arg))) {
195 flow(Dst: getOriginsList(E: *CCE), Src: ArgList, /*Kill=*/true);
196 return;
197 }
198 }
199 handleFunctionCall(Call: CCE, FD: CCE->getConstructor(),
200 Args: {CCE->getArgs(), CCE->getNumArgs()},
201 /*IsGslConstruction=*/false);
202}
203
204void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) {
205 // Flows origins from the initializer expression to the field.
206 // Example: `MyObj(std::string s) : view(s) {}`
207 if (const FieldDecl *FD = CII->getAnyMember())
208 killAndFlowOrigin(D: *FD, S: *CII->getInit());
209}
210
211void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
212 // Specifically for conversion operators,
213 // like `std::string_view p = std::string{};`
214 if (isGslPointerType(QT: MCE->getType()) &&
215 isa_and_present<CXXConversionDecl>(Val: MCE->getCalleeDecl()) &&
216 isGslOwnerType(QT: MCE->getImplicitObjectArgument()->getType())) {
217 // The argument is the implicit object itself.
218 handleFunctionCall(Call: MCE, FD: MCE->getMethodDecl(),
219 Args: {MCE->getImplicitObjectArgument()},
220 /*IsGslConstruction=*/true);
221 return;
222 }
223 if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
224 // Construct the argument list, with the implicit 'this' object as the
225 // first argument.
226 llvm::SmallVector<const Expr *, 4> Args;
227 Args.push_back(Elt: MCE->getImplicitObjectArgument());
228 Args.append(in_start: MCE->getArgs(), in_end: MCE->getArgs() + MCE->getNumArgs());
229
230 handleFunctionCall(Call: MCE, FD: Method, Args, /*IsGslConstruction=*/false);
231 }
232}
233
234void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
235 auto *MD = ME->getMemberDecl();
236 if (isa<FieldDecl>(Val: MD) && doesDeclHaveStorage(D: MD)) {
237 assert(ME->isGLValue() && "Field member should be GL value");
238 OriginList *Dst = getOriginsList(E: *ME);
239 assert(Dst && "Field member should have an origin list as it is GL value");
240 OriginList *Src = getOriginsList(E: *ME->getBase());
241 assert(Src && "Base expression should be a pointer/reference type");
242 // The field's glvalue (outermost origin) holds the same loans as the base
243 // expression.
244 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
245 args: Dst->getOuterOriginID(), args: Src->getOuterOriginID(),
246 /*Kill=*/args: true));
247 }
248}
249
250void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
251 handleFunctionCall(Call: CE, FD: CE->getDirectCallee(),
252 Args: {CE->getArgs(), CE->getNumArgs()});
253}
254
255void FactsGenerator::VisitCXXNullPtrLiteralExpr(
256 const CXXNullPtrLiteralExpr *N) {
257 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
258 /// pointers can use the same type of loan.
259 getOriginsList(E: *N);
260}
261
262void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
263 OriginList *Dest = getOriginsList(E: *ICE);
264 if (!Dest)
265 return;
266 const Expr *SubExpr = ICE->getSubExpr();
267 OriginList *Src = getOriginsList(E: *SubExpr);
268
269 switch (ICE->getCastKind()) {
270 case CK_LValueToRValue:
271 // TODO: Decide what to do for x-values here.
272 if (!SubExpr->isLValue())
273 return;
274
275 assert(Src && "LValue being cast to RValue has no origin list");
276 // The result of an LValue-to-RValue cast on a pointer lvalue (like `q` in
277 // `int *p, *q; p = q;`) should propagate the inner origin (what the pointer
278 // points to), not the outer origin (the pointer's storage location). Strip
279 // the outer lvalue origin.
280 flow(Dst: getOriginsList(E: *ICE), Src: getRValueOrigins(E: SubExpr, List: Src),
281 /*Kill=*/true);
282 return;
283 case CK_NullToPointer:
284 getOriginsList(E: *ICE);
285 // TODO: Flow into them a null origin.
286 return;
287 case CK_NoOp:
288 case CK_ConstructorConversion:
289 case CK_UserDefinedConversion:
290 flow(Dst: Dest, Src, /*Kill=*/true);
291 return;
292 case CK_UncheckedDerivedToBase:
293 case CK_DerivedToBase:
294 // It is possible that the derived class and base class have different
295 // gsl::Pointer annotations. Skip if their origin shape differ.
296 if (Dest && Src && Dest->getLength() == Src->getLength())
297 flow(Dst: Dest, Src, /*Kill=*/true);
298 return;
299 case CK_FunctionToPointerDecay:
300 case CK_BuiltinFnToFnPtr:
301 case CK_ArrayToPointerDecay:
302 // Ignore function-to-pointer decays.
303 return;
304 default:
305 return;
306 }
307}
308
309void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) {
310 switch (UO->getOpcode()) {
311 case UO_AddrOf: {
312 const Expr *SubExpr = UO->getSubExpr();
313 // The origin of an address-of expression (e.g., &x) is the origin of
314 // its sub-expression (x). This fact will cause the dataflow analysis
315 // to propagate any loans held by the sub-expression's origin to the
316 // origin of this UnaryOperator expression.
317 killAndFlowOrigin(D: *UO, S: *SubExpr);
318 return;
319 }
320 case UO_Deref: {
321 const Expr *SubExpr = UO->getSubExpr();
322 killAndFlowOrigin(D: *UO, S: *SubExpr);
323 return;
324 }
325 default:
326 return;
327 }
328}
329
330void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
331 if (const Expr *RetExpr = RS->getRetValue()) {
332 if (OriginList *List = getOriginsList(E: *RetExpr))
333 for (OriginList *L = List; L != nullptr; L = L->peelOuterOrigin())
334 EscapesInCurrentBlock.push_back(Elt: FactMgr.createFact<ReturnEscapeFact>(
335 args: L->getOuterOriginID(), args&: RetExpr));
336 }
337}
338
339void FactsGenerator::handleAssignment(const Expr *LHSExpr,
340 const Expr *RHSExpr) {
341 LHSExpr = LHSExpr->IgnoreParenImpCasts();
342 OriginList *LHSList = nullptr;
343
344 if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(Val: LHSExpr)) {
345 LHSList = getOriginsList(E: *DRE_LHS);
346 assert(LHSList && "LHS is a DRE and should have an origin list");
347 }
348 // Handle assignment to member fields (e.g., `this->view = s` or `view = s`).
349 // This enables detection of dangling fields when local values escape to
350 // fields.
351 if (const auto *ME_LHS = dyn_cast<MemberExpr>(Val: LHSExpr)) {
352 LHSList = getOriginsList(E: *ME_LHS);
353 assert(LHSList && "LHS is a MemberExpr and should have an origin list");
354 }
355 if (!LHSList)
356 return;
357 OriginList *RHSList = getOriginsList(E: *RHSExpr);
358 // For operator= with reference parameters (e.g.,
359 // `View& operator=(const View&)`), the RHS argument stays an lvalue,
360 // unlike built-in assignment where LValueToRValue cast strips the outer
361 // lvalue origin. Strip it manually to get the actual value origins being
362 // assigned.
363 RHSList = getRValueOrigins(E: RHSExpr, List: RHSList);
364
365 if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(Val: LHSExpr))
366 markUseAsWrite(DRE: DRE_LHS);
367 // Kill the old loans of the destination origin and flow the new loans
368 // from the source origin.
369 flow(Dst: LHSList->peelOuterOrigin(), Src: RHSList, /*Kill=*/true);
370}
371
372void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
373 // TODO: Handle pointer arithmetic (e.g., `p + 1` or `1 + p`) where the
374 // result should have the same loans as the pointer operand.
375 if (BO->isCompoundAssignmentOp())
376 return;
377 handleUse(E: BO->getRHS());
378 if (BO->isAssignmentOp())
379 handleAssignment(LHSExpr: BO->getLHS(), RHSExpr: BO->getRHS());
380 // TODO: Handle assignments involving dereference like `*p = q`.
381}
382
383void FactsGenerator::VisitConditionalOperator(const ConditionalOperator *CO) {
384 if (hasOrigins(E: CO)) {
385 // Merge origins from both branches of the conditional operator.
386 // We kill to clear the initial state and merge both origins into it.
387 killAndFlowOrigin(D: *CO, S: *CO->getTrueExpr());
388 flowOrigin(D: *CO, S: *CO->getFalseExpr());
389 }
390}
391
392void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
393 // Assignment operators have special "kill-then-propagate" semantics
394 // and are handled separately.
395 if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2 &&
396 hasOrigins(QT: OCE->getArg(Arg: 0)->getType())) {
397 handleAssignment(LHSExpr: OCE->getArg(Arg: 0), RHSExpr: OCE->getArg(Arg: 1));
398 return;
399 }
400 VisitCallExpr(CE: OCE);
401}
402
403void FactsGenerator::VisitCXXFunctionalCastExpr(
404 const CXXFunctionalCastExpr *FCE) {
405 // Check if this is a test point marker. If so, we are done with this
406 // expression.
407 if (handleTestPoint(FCE))
408 return;
409 if (isGslPointerType(QT: FCE->getType()))
410 killAndFlowOrigin(D: *FCE, S: *FCE->getSubExpr());
411}
412
413void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {
414 if (!hasOrigins(E: ILE))
415 return;
416 // For list initialization with a single element, like `View{...}`, the
417 // origin of the list itself is the origin of its single element.
418 if (ILE->getNumInits() == 1)
419 killAndFlowOrigin(D: *ILE, S: *ILE->getInit(Init: 0));
420}
421
422void FactsGenerator::VisitCXXBindTemporaryExpr(
423 const CXXBindTemporaryExpr *BTE) {
424 killAndFlowOrigin(D: *BTE, S: *BTE->getSubExpr());
425}
426
427void FactsGenerator::VisitMaterializeTemporaryExpr(
428 const MaterializeTemporaryExpr *MTE) {
429 assert(MTE->isGLValue());
430 OriginList *MTEList = getOriginsList(E: *MTE);
431 if (!MTEList)
432 return;
433 OriginList *SubExprList = getOriginsList(E: *MTE->getSubExpr());
434 assert((!SubExprList ||
435 MTEList->getLength() == (SubExprList->getLength() + 1)) &&
436 "MTE top level origin should contain a loan to the MTE itself");
437
438 OriginList *RValMTEList = getRValueOrigins(E: MTE, List: MTEList);
439 flow(Dst: RValMTEList, Src: SubExprList, /*Kill=*/true);
440 OriginID OuterMTEID = MTEList->getOuterOriginID();
441 if (MTE->getStorageDuration() == SD_FullExpression) {
442 // Issue a loan to MTE for the storage location represented by MTE.
443 const Loan *L = createLoan(FactMgr, MTE);
444 CurrentBlockFacts.push_back(
445 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args&: OuterMTEID));
446 }
447}
448
449void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) {
450 // The lambda gets a single merged origin that aggregates all captured
451 // pointer-like origins. Currently we only need to detect whether the lambda
452 // outlives any capture.
453 OriginList *LambdaList = getOriginsList(E: *LE);
454 if (!LambdaList)
455 return;
456 bool Kill = true;
457 for (const Expr *Init : LE->capture_inits()) {
458 if (!Init)
459 continue;
460 OriginList *InitList = getOriginsList(E: *Init);
461 if (!InitList)
462 continue;
463 // FIXME: Consider flowing all origin levels once lambdas support more than
464 // one origin. Currently only the outermost origin is flowed, so by-ref
465 // captures like `[&p]` (where p is string_view) miss inner-level
466 // invalidation.
467 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
468 args: LambdaList->getOuterOriginID(), args: InitList->getOuterOriginID(), args&: Kill));
469 Kill = false;
470 }
471}
472
473bool FactsGenerator::escapesViaReturn(OriginID OID) const {
474 return llvm::any_of(Range: EscapesInCurrentBlock, P: [OID](const Fact *F) {
475 if (const auto *EF = F->getAs<ReturnEscapeFact>())
476 return EF->getEscapedOriginID() == OID;
477 return false;
478 });
479}
480
481void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
482 const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
483 if (!LifetimeEndsVD)
484 return;
485 // Expire the origin when its variable's lifetime ends to ensure liveness
486 // doesn't persist through loop back-edges.
487 std::optional<OriginID> ExpiredOID;
488 if (OriginList *List = getOriginsList(D: *LifetimeEndsVD)) {
489 OriginID OID = List->getOuterOriginID();
490 // Skip origins that escape via return; the escape checker needs their loans
491 // to remain until the return statement is processed.
492 if (!escapesViaReturn(OID))
493 ExpiredOID = OID;
494 }
495 // Iterate through all loans to see if any expire.
496 for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
497 if (const auto *BL = dyn_cast<PathLoan>(Val: Loan)) {
498 // Check if the loan is for a stack variable and if that variable
499 // is the one being destructed.
500 const AccessPath AP = BL->getAccessPath();
501 const ValueDecl *Path = AP.getAsValueDecl();
502 if (Path == LifetimeEndsVD)
503 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<ExpireFact>(
504 args: BL->getID(), args: LifetimeEnds.getTriggerStmt()->getEndLoc(),
505 args&: ExpiredOID));
506 }
507 }
508}
509
510void FactsGenerator::handleFullExprCleanup(
511 const CFGFullExprCleanup &FullExprCleanup) {
512 // Iterate through all loans to see if any expire.
513 for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
514 if (const auto *PL = dyn_cast<PathLoan>(Val: Loan)) {
515 // Check if the loan is for a temporary materialization and if that
516 // storage location is the one being destructed.
517 const AccessPath &AP = PL->getAccessPath();
518 const MaterializeTemporaryExpr *Path = AP.getAsMaterializeTemporaryExpr();
519 if (!Path)
520 continue;
521 if (llvm::is_contained(Range: FullExprCleanup.getExpiringMTEs(), Element: Path)) {
522 CurrentBlockFacts.push_back(
523 Elt: FactMgr.createFact<ExpireFact>(args: PL->getID(), args: Path->getEndLoc()));
524 }
525 }
526 }
527}
528
529void FactsGenerator::handleExitBlock() {
530 for (const Origin &O : FactMgr.getOriginMgr().getOrigins())
531 if (auto *FD = dyn_cast_if_present<FieldDecl>(Val: O.getDecl()))
532 // Create FieldEscapeFacts for all field origins that remain live at exit.
533 EscapesInCurrentBlock.push_back(
534 Elt: FactMgr.createFact<FieldEscapeFact>(args: O.ID, args&: FD));
535 else if (auto *VD = dyn_cast_if_present<VarDecl>(Val: O.getDecl())) {
536 // Create GlobalEscapeFacts for all origins with global-storage that
537 // remain live at exit.
538 if (VD->hasGlobalStorage()) {
539 EscapesInCurrentBlock.push_back(
540 Elt: FactMgr.createFact<GlobalEscapeFact>(args: O.ID, args&: VD));
541 }
542 }
543}
544
545void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
546 assert(isGslPointerType(CCE->getType()));
547 if (CCE->getNumArgs() != 1)
548 return;
549
550 const Expr *Arg = CCE->getArg(Arg: 0);
551 if (isGslPointerType(QT: Arg->getType())) {
552 OriginList *ArgList = getOriginsList(E: *Arg);
553 assert(ArgList && "GSL pointer argument should have an origin list");
554 // GSL pointer is constructed from another gsl pointer.
555 // Example:
556 // View(View v);
557 // View(const View &v);
558 ArgList = getRValueOrigins(E: Arg, List: ArgList);
559 flow(Dst: getOriginsList(E: *CCE), Src: ArgList, /*Kill=*/true);
560 } else if (Arg->getType()->isPointerType()) {
561 // GSL pointer is constructed from a raw pointer. Flow only the outermost
562 // raw pointer. Example:
563 // View(const char*);
564 // Span<int*>(const in**);
565 OriginList *ArgList = getOriginsList(E: *Arg);
566 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
567 args: getOriginsList(E: *CCE)->getOuterOriginID(), args: ArgList->getOuterOriginID(),
568 /*Kill=*/args: true));
569 } else {
570 // This could be a new borrow.
571 // TODO: Add code example here.
572 handleFunctionCall(Call: CCE, FD: CCE->getConstructor(),
573 Args: {CCE->getArgs(), CCE->getNumArgs()},
574 /*IsGslConstruction=*/true);
575 }
576}
577
578void FactsGenerator::handleMovedArgsInCall(const FunctionDecl *FD,
579 ArrayRef<const Expr *> Args) {
580 unsigned IsInstance = 0;
581 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD);
582 MD && MD->isInstance() && !isa<CXXConstructorDecl>(Val: FD)) {
583 IsInstance = 1;
584 // std::unique_ptr::release() transfers ownership.
585 // Treat it as a move to prevent false-positive warnings when the unique_ptr
586 // destructor runs after ownership has been transferred.
587 if (isUniquePtrRelease(MD: *MD)) {
588 const Expr *UniquePtrExpr = Args[0];
589 OriginList *MovedOrigins = getOriginsList(E: *UniquePtrExpr);
590 if (MovedOrigins)
591 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<MovedOriginFact>(
592 args&: UniquePtrExpr, args: MovedOrigins->getOuterOriginID()));
593 }
594 }
595
596 // Skip 'this' arg as it cannot be moved.
597 for (unsigned I = IsInstance;
598 I < Args.size() && I < FD->getNumParams() + IsInstance; ++I) {
599 const ParmVarDecl *PVD = FD->getParamDecl(i: I - IsInstance);
600 if (!PVD->getType()->isRValueReferenceType())
601 continue;
602 const Expr *Arg = Args[I];
603 OriginList *MovedOrigins = getOriginsList(E: *Arg);
604 assert(MovedOrigins->getLength() >= 1 &&
605 "unexpected length for r-value reference param");
606 // Arg is being moved to this parameter. Mark the origin as moved.
607 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<MovedOriginFact>(
608 args&: Arg, args: MovedOrigins->getOuterOriginID()));
609 }
610}
611
612void FactsGenerator::handleInvalidatingCall(const Expr *Call,
613 const FunctionDecl *FD,
614 ArrayRef<const Expr *> Args) {
615 const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD);
616 if (!MD || !MD->isInstance())
617 return;
618
619 if (!isContainerInvalidationMethod(MD: *MD))
620 return;
621 // Heuristics to turn-down false positives.
622 auto *DRE = dyn_cast<DeclRefExpr>(Val: Args[0]);
623 if (!DRE || DRE->getDecl()->getType()->isReferenceType())
624 return;
625
626 OriginList *ThisList = getOriginsList(E: *Args[0]);
627 if (ThisList)
628 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<InvalidateOriginFact>(
629 args: ThisList->getOuterOriginID(), args&: Call));
630}
631
632void FactsGenerator::handleFunctionCall(const Expr *Call,
633 const FunctionDecl *FD,
634 ArrayRef<const Expr *> Args,
635 bool IsGslConstruction) {
636 OriginList *CallList = getOriginsList(E: *Call);
637 // Ignore functions returning values with no origin.
638 FD = getDeclWithMergedLifetimeBoundAttrs(FD);
639 if (!FD)
640 return;
641 // All arguments to a function are a use of the corresponding expressions.
642 for (const Expr *Arg : Args)
643 handleUse(E: Arg);
644 handleInvalidatingCall(Call, FD, Args);
645 handleMovedArgsInCall(FD, Args);
646 if (!CallList)
647 return;
648 auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
649 const ParmVarDecl *PVD = nullptr;
650 if (const auto *Method = dyn_cast<CXXMethodDecl>(Val: FD);
651 Method && Method->isInstance()) {
652 if (I == 0)
653 // For the 'this' argument, the attribute is on the method itself.
654 return implicitObjectParamIsLifetimeBound(FD: Method) ||
655 shouldTrackImplicitObjectArg(
656 Callee: Method, /*RunningUnderLifetimeSafety=*/true);
657 if ((I - 1) < Method->getNumParams())
658 // For explicit arguments, find the corresponding parameter
659 // declaration.
660 PVD = Method->getParamDecl(i: I - 1);
661 } else if (I == 0 && shouldTrackFirstArgument(FD)) {
662 return true;
663 } else if (I < FD->getNumParams()) {
664 // For free functions or static methods.
665 PVD = FD->getParamDecl(i: I);
666 }
667 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
668 };
669 auto shouldTrackPointerImplicitObjectArg = [FD](unsigned I) -> bool {
670 const auto *Method = dyn_cast<CXXMethodDecl>(Val: FD);
671 if (!Method || !Method->isInstance())
672 return false;
673 return I == 0 &&
674 isGslPointerType(QT: Method->getFunctionObjectParameterType()) &&
675 shouldTrackImplicitObjectArg(Callee: Method,
676 /*RunningUnderLifetimeSafety=*/true);
677 };
678 if (Args.empty())
679 return;
680 bool KillSrc = true;
681 for (unsigned I = 0; I < Args.size(); ++I) {
682 OriginList *ArgList = getOriginsList(E: *Args[I]);
683 if (!ArgList)
684 continue;
685 if (IsGslConstruction) {
686 // TODO: document with code example.
687 // std::string_view(const std::string_view& from)
688 if (isGslPointerType(QT: Args[I]->getType())) {
689 assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
690 ArgList = getRValueOrigins(E: Args[I], List: ArgList);
691 }
692 if (isGslOwnerType(QT: Args[I]->getType())) {
693 // GSL construction creates a view that borrows from arguments.
694 // This implies flowing origins through the list structure.
695 flow(Dst: CallList, Src: ArgList, Kill: KillSrc);
696 KillSrc = false;
697 }
698 } else if (shouldTrackPointerImplicitObjectArg(I)) {
699 assert(ArgList->getLength() >= 2 &&
700 "Object arg of pointer type should have atleast two origins");
701 // See through the GSLPointer reference to see the pointer's value.
702 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
703 args: CallList->getOuterOriginID(),
704 args: ArgList->peelOuterOrigin()->getOuterOriginID(), args&: KillSrc));
705 KillSrc = false;
706 } else if (IsArgLifetimeBound(I)) {
707 // Lifetimebound on a non-GSL-ctor function means the returned
708 // pointer/reference itself must not outlive the arguments. This
709 // only constraints the top-level origin.
710 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
711 args: CallList->getOuterOriginID(), args: ArgList->getOuterOriginID(), args&: KillSrc));
712 KillSrc = false;
713 }
714 }
715}
716
717/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
718/// If so, creates a `TestPointFact` and returns true.
719bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
720 if (!FCE->getType()->isVoidType())
721 return false;
722
723 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
724 if (const auto *SL = dyn_cast<StringLiteral>(Val: SubExpr)) {
725 llvm::StringRef LiteralValue = SL->getString();
726 const std::string Prefix = "__lifetime_test_point_";
727
728 if (LiteralValue.starts_with(Prefix)) {
729 StringRef Annotation = LiteralValue.drop_front(N: Prefix.length());
730 CurrentBlockFacts.push_back(
731 Elt: FactMgr.createFact<TestPointFact>(args&: Annotation));
732 return true;
733 }
734 }
735 return false;
736}
737
738void FactsGenerator::handleUse(const Expr *E) {
739 OriginList *List = getOriginsList(E: *E);
740 if (!List)
741 return;
742 // For DeclRefExpr: Remove the outer layer of origin which borrows from the
743 // decl directly (e.g., when this is not a reference). This is a use of the
744 // underlying decl.
745 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: E);
746 DRE && !DRE->getDecl()->getType()->isReferenceType())
747 List = getRValueOrigins(E: DRE, List);
748 // Skip if there is no inner origin (e.g., when it is not a pointer type).
749 if (!List)
750 return;
751 if (!UseFacts.contains(Val: E)) {
752 UseFact *UF = FactMgr.createFact<UseFact>(args&: E, args&: List);
753 CurrentBlockFacts.push_back(Elt: UF);
754 UseFacts[E] = UF;
755 }
756}
757
758void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
759 if (UseFacts.contains(Val: DRE))
760 UseFacts[DRE]->markAsWritten();
761}
762
763// Creates an IssueFact for a new placeholder loan for each pointer or reference
764// parameter at the function's entry.
765llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
766 const auto *FD = dyn_cast<FunctionDecl>(Val: AC.getDecl());
767 if (!FD)
768 return {};
769
770 llvm::SmallVector<Fact *> PlaceholderLoanFacts;
771 if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) {
772 OriginList *List = *ThisOrigins;
773 const PlaceholderLoan *L = FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(
774 args: cast<CXXMethodDecl>(Val: FD));
775 PlaceholderLoanFacts.push_back(
776 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
777 }
778 for (const ParmVarDecl *PVD : FD->parameters()) {
779 OriginList *List = getOriginsList(D: *PVD);
780 if (!List)
781 continue;
782 const PlaceholderLoan *L =
783 FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(args&: PVD);
784 PlaceholderLoanFacts.push_back(
785 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
786 }
787 return PlaceholderLoanFacts;
788}
789
790} // namespace clang::lifetimes::internal
791