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