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/OperationKinds.h"
13#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
14#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
15#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
16#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
17#include "clang/Analysis/Analyses/PostOrderCFGView.h"
18#include "llvm/Support/Casting.h"
19#include "llvm/Support/Signals.h"
20#include "llvm/Support/TimeProfiler.h"
21
22namespace clang::lifetimes::internal {
23using llvm::isa_and_present;
24
25OriginList *FactsGenerator::getOriginsList(const ValueDecl &D) {
26 return FactMgr.getOriginMgr().getOrCreateList(D: &D);
27}
28OriginList *FactsGenerator::getOriginsList(const Expr &E) {
29 return FactMgr.getOriginMgr().getOrCreateList(E: &E);
30}
31
32/// Propagates origin information from Src to Dst through all levels of
33/// indirection, creating OriginFlowFacts at each level.
34///
35/// This function enforces a critical type-safety invariant: both lists must
36/// have the same shape (same depth/structure). This invariant ensures that
37/// origins flow only between compatible types during expression evaluation.
38///
39/// Examples:
40/// - `int* p = &x;` flows origins from `&x` (depth 1) to `p` (depth 1)
41/// - `int** pp = &p;` flows origins from `&p` (depth 2) to `pp` (depth 2)
42/// * Level 1: pp <- p's address
43/// * Level 2: (*pp) <- what p points to (i.e., &x)
44/// - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
45void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) {
46 if (!Dst)
47 return;
48 assert(Src &&
49 "Dst is non-null but Src is null. List must have the same length");
50 assert(Dst->getLength() == Src->getLength() &&
51 "Lists must have the same length");
52
53 while (Dst && Src) {
54 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
55 args: Dst->getOuterOriginID(), args: Src->getOuterOriginID(), args&: Kill));
56 Dst = Dst->peelOuterOrigin();
57 Src = Src->peelOuterOrigin();
58 }
59}
60
61/// Creates a loan for the storage path of a given declaration reference.
62/// This function should be called whenever a DeclRefExpr represents a borrow.
63/// \param DRE The declaration reference expression that initiates the borrow.
64/// \return The new Loan on success, nullptr otherwise.
65static const PathLoan *createLoan(FactManager &FactMgr,
66 const DeclRefExpr *DRE) {
67 if (const auto *VD = dyn_cast<ValueDecl>(Val: DRE->getDecl())) {
68 AccessPath Path(VD);
69 // The loan is created at the location of the DeclRefExpr.
70 return FactMgr.getLoanMgr().createLoan<PathLoan>(args&: Path, args&: DRE);
71 }
72 return nullptr;
73}
74
75/// Creates a loan for the storage location of a temporary object.
76/// \param MTE The MaterializeTemporaryExpr that represents the temporary
77/// binding. \return The new Loan.
78static const PathLoan *createLoan(FactManager &FactMgr,
79 const MaterializeTemporaryExpr *MTE) {
80 AccessPath Path(MTE);
81 return FactMgr.getLoanMgr().createLoan<PathLoan>(args&: Path, args&: MTE);
82}
83
84/// Try to find a CXXBindTemporaryExpr that descends from MTE, stripping away
85/// any implicit casts.
86/// \param MTE MaterializeTemporaryExpr whose descendants we are interested in.
87/// \return Pointer to descendant CXXBindTemporaryExpr or nullptr when not
88/// found.
89static const CXXBindTemporaryExpr *
90getChildBinding(const MaterializeTemporaryExpr *MTE) {
91 const Expr *Child = MTE->getSubExpr()->IgnoreImpCasts();
92 return dyn_cast<CXXBindTemporaryExpr>(Val: Child);
93}
94
95void FactsGenerator::run() {
96 llvm::TimeTraceScope TimeProfile("FactGenerator");
97 const CFG &Cfg = *AC.getCFG();
98 llvm::SmallVector<Fact *> PlaceholderLoanFacts = issuePlaceholderLoans();
99 // Iterate through the CFG blocks in reverse post-order to ensure that
100 // initializations and destructions are processed in the correct sequence.
101 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
102 CurrentBlockFacts.clear();
103 EscapesInCurrentBlock.clear();
104 if (Block == &Cfg.getEntry())
105 CurrentBlockFacts.append(in_start: PlaceholderLoanFacts.begin(),
106 in_end: PlaceholderLoanFacts.end());
107 for (unsigned I = 0; I < Block->size(); ++I) {
108 const CFGElement &Element = Block->Elements[I];
109 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
110 Visit(S: CS->getStmt());
111 else if (std::optional<CFGInitializer> Initializer =
112 Element.getAs<CFGInitializer>())
113 handleCXXCtorInitializer(CII: Initializer->getInitializer());
114 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
115 Element.getAs<CFGLifetimeEnds>())
116 handleLifetimeEnds(LifetimeEnds: *LifetimeEnds);
117 else if (std::optional<CFGTemporaryDtor> TemporaryDtor =
118 Element.getAs<CFGTemporaryDtor>())
119 handleTemporaryDtor(TemporaryDtor: *TemporaryDtor);
120 }
121 if (Block == &Cfg.getExit())
122 handleExitBlock();
123
124 CurrentBlockFacts.append(in_start: EscapesInCurrentBlock.begin(),
125 in_end: EscapesInCurrentBlock.end());
126 FactMgr.addBlockFacts(B: Block, NewFacts: CurrentBlockFacts);
127 }
128}
129
130/// Simulates LValueToRValue conversion by peeling the outer lvalue origin
131/// if the expression is a GLValue. For pointer/view GLValues, this strips
132/// the origin representing the storage location to get the origins of the
133/// pointed-to value.
134///
135/// Example: For `View& v`, returns the origin of what v points to, not v's
136/// storage.
137static OriginList *getRValueOrigins(const Expr *E, OriginList *List) {
138 if (!List)
139 return nullptr;
140 return E->isGLValue() ? List->peelOuterOrigin() : List;
141}
142
143void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
144 for (const Decl *D : DS->decls())
145 if (const auto *VD = dyn_cast<VarDecl>(Val: D))
146 if (const Expr *InitExpr = VD->getInit()) {
147 OriginList *VDList = getOriginsList(D: *VD);
148 if (!VDList)
149 continue;
150 OriginList *InitList = getOriginsList(E: *InitExpr);
151 assert(InitList && "VarDecl had origins but InitExpr did not");
152 flow(Dst: VDList, Src: InitList, /*Kill=*/true);
153 }
154}
155
156void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
157 // Skip function references as their lifetimes are not interesting. Skip non
158 // GLValues (like EnumConstants).
159 if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())
160 return;
161 handleUse(DRE);
162 // For all declarations with storage (non-references), we issue a loan
163 // representing the borrow of the variable's storage itself.
164 //
165 // Examples:
166 // - `int x; x` issues loan to x's storage
167 // - `int* p; p` issues loan to p's storage (the pointer variable)
168 // - `View v; v` issues loan to v's storage (the view object)
169 // - `int& r = x; r` issues no loan (r has no storage, it's an alias to x)
170 if (doesDeclHaveStorage(D: DRE->getDecl())) {
171 const Loan *L = createLoan(FactMgr, DRE);
172 assert(L);
173 OriginList *List = getOriginsList(E: *DRE);
174 assert(List &&
175 "gl-value DRE of non-pointer type should have an origin list");
176 // This loan specifically tracks borrowing the variable's storage location
177 // itself and is issued to outermost origin (List->OID).
178 CurrentBlockFacts.push_back(
179 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
180 }
181}
182
183void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
184 if (isGslPointerType(QT: CCE->getType())) {
185 handleGSLPointerConstruction(CCE);
186 return;
187 }
188}
189
190void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) {
191 // Flows origins from the initializer expression to the field.
192 // Example: `MyObj(std::string s) : view(s) {}`
193 if (const FieldDecl *FD = CII->getAnyMember())
194 killAndFlowOrigin(D: *FD, S: *CII->getInit());
195}
196
197void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
198 // Specifically for conversion operators,
199 // like `std::string_view p = std::string{};`
200 if (isGslPointerType(QT: MCE->getType()) &&
201 isa_and_present<CXXConversionDecl>(Val: MCE->getCalleeDecl()) &&
202 isGslOwnerType(QT: MCE->getImplicitObjectArgument()->getType())) {
203 // The argument is the implicit object itself.
204 handleFunctionCall(Call: MCE, FD: MCE->getMethodDecl(),
205 Args: {MCE->getImplicitObjectArgument()},
206 /*IsGslConstruction=*/true);
207 return;
208 }
209 if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
210 // Construct the argument list, with the implicit 'this' object as the
211 // first argument.
212 llvm::SmallVector<const Expr *, 4> Args;
213 Args.push_back(Elt: MCE->getImplicitObjectArgument());
214 Args.append(in_start: MCE->getArgs(), in_end: MCE->getArgs() + MCE->getNumArgs());
215
216 handleFunctionCall(Call: MCE, FD: Method, Args, /*IsGslConstruction=*/false);
217 }
218}
219
220void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
221 auto *MD = ME->getMemberDecl();
222 if (isa<FieldDecl>(Val: MD) && doesDeclHaveStorage(D: MD)) {
223 assert(ME->isGLValue() && "Field member should be GL value");
224 OriginList *Dst = getOriginsList(E: *ME);
225 assert(Dst && "Field member should have an origin list as it is GL value");
226 OriginList *Src = getOriginsList(E: *ME->getBase());
227 assert(Src && "Base expression should be a pointer/reference type");
228 // The field's glvalue (outermost origin) holds the same loans as the base
229 // expression.
230 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
231 args: Dst->getOuterOriginID(), args: Src->getOuterOriginID(),
232 /*Kill=*/args: true));
233 }
234}
235
236static bool isStdMove(const FunctionDecl *FD) {
237 return FD && FD->isInStdNamespace() && FD->getIdentifier() &&
238 FD->getName() == "move";
239}
240
241void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
242 handleFunctionCall(Call: CE, FD: CE->getDirectCallee(),
243 Args: {CE->getArgs(), CE->getNumArgs()});
244 // Track declarations that are moved via std::move.
245 // This is a flow-insensitive approximation: once a declaration is moved
246 // anywhere in the function, it's treated as moved everywhere. We do not
247 // generate expire facts for moved decls to avoid false alarms.
248 if (isStdMove(FD: CE->getDirectCallee()))
249 if (CE->getNumArgs() == 1)
250 if (auto *DRE =
251 dyn_cast<DeclRefExpr>(Val: CE->getArg(Arg: 0)->IgnoreParenImpCasts()))
252 MovedDecls.insert(V: DRE->getDecl());
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 if (BO->isAssignmentOp())
378 handleAssignment(LHSExpr: BO->getLHS(), RHSExpr: BO->getRHS());
379 // TODO: Handle assignments involving dereference like `*p = q`.
380}
381
382void FactsGenerator::VisitConditionalOperator(const ConditionalOperator *CO) {
383 if (hasOrigins(E: CO)) {
384 // Merge origins from both branches of the conditional operator.
385 // We kill to clear the initial state and merge both origins into it.
386 killAndFlowOrigin(D: *CO, S: *CO->getTrueExpr());
387 flowOrigin(D: *CO, S: *CO->getFalseExpr());
388 }
389}
390
391void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
392 // Assignment operators have special "kill-then-propagate" semantics
393 // and are handled separately.
394 if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2 &&
395 hasOrigins(QT: OCE->getArg(Arg: 0)->getType())) {
396 handleAssignment(LHSExpr: OCE->getArg(Arg: 0), RHSExpr: OCE->getArg(Arg: 1));
397 return;
398 }
399 VisitCallExpr(CE: OCE);
400}
401
402void FactsGenerator::VisitCXXFunctionalCastExpr(
403 const CXXFunctionalCastExpr *FCE) {
404 // Check if this is a test point marker. If so, we are done with this
405 // expression.
406 if (handleTestPoint(FCE))
407 return;
408 if (isGslPointerType(QT: FCE->getType()))
409 killAndFlowOrigin(D: *FCE, S: *FCE->getSubExpr());
410}
411
412void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {
413 if (!hasOrigins(E: ILE))
414 return;
415 // For list initialization with a single element, like `View{...}`, the
416 // origin of the list itself is the origin of its single element.
417 if (ILE->getNumInits() == 1)
418 killAndFlowOrigin(D: *ILE, S: *ILE->getInit(Init: 0));
419}
420
421void FactsGenerator::VisitCXXBindTemporaryExpr(
422 const CXXBindTemporaryExpr *BTE) {
423 killAndFlowOrigin(D: *BTE, S: *BTE->getSubExpr());
424}
425
426void FactsGenerator::VisitMaterializeTemporaryExpr(
427 const MaterializeTemporaryExpr *MTE) {
428 assert(MTE->isGLValue());
429 OriginList *MTEList = getOriginsList(E: *MTE);
430 if (!MTEList)
431 return;
432 OriginList *SubExprList = getOriginsList(E: *MTE->getSubExpr());
433 assert((!SubExprList ||
434 MTEList->getLength() == (SubExprList->getLength() + 1)) &&
435 "MTE top level origin should contain a loan to the MTE itself");
436
437 OriginList *RValMTEList = getRValueOrigins(E: MTE, List: MTEList);
438 flow(Dst: RValMTEList, Src: SubExprList, /*Kill=*/true);
439 OriginID OuterMTEID = MTEList->getOuterOriginID();
440 if (getChildBinding(MTE)) {
441 // Issue a loan to MTE for the storage location represented by MTE.
442 const Loan *L = createLoan(FactMgr, MTE);
443 CurrentBlockFacts.push_back(
444 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args&: OuterMTEID));
445 }
446}
447
448void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
449 /// TODO: Handle loans to temporaries.
450 const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
451 if (!LifetimeEndsVD)
452 return;
453 // Iterate through all loans to see if any expire.
454 for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
455 if (const auto *BL = dyn_cast<PathLoan>(Val: Loan)) {
456 // Skip loans for declarations that have been moved. When a value is
457 // moved, the original owner no longer has ownership and its destruction
458 // should not cause the loan to expire, preventing false positives.
459 if (MovedDecls.contains(V: BL->getAccessPath().getAsValueDecl()))
460 continue;
461 // Check if the loan is for a stack variable and if that variable
462 // is the one being destructed.
463 const AccessPath AP = BL->getAccessPath();
464 const ValueDecl *Path = AP.getAsValueDecl();
465 if (Path == LifetimeEndsVD)
466 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<ExpireFact>(
467 args: BL->getID(), args: LifetimeEnds.getTriggerStmt()->getEndLoc()));
468 }
469 }
470}
471
472void FactsGenerator::handleTemporaryDtor(
473 const CFGTemporaryDtor &TemporaryDtor) {
474 const CXXBindTemporaryExpr *ExpiringBTE =
475 TemporaryDtor.getBindTemporaryExpr();
476 if (!ExpiringBTE)
477 return;
478 // Iterate through all loans to see if any expire.
479 for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
480 if (const auto *PL = dyn_cast<PathLoan>(Val: Loan)) {
481 // Check if the loan is for a temporary materialization and if that
482 // storage location is the one being destructed.
483 const AccessPath &AP = PL->getAccessPath();
484 const MaterializeTemporaryExpr *Path = AP.getAsMaterializeTemporaryExpr();
485 if (!Path)
486 continue;
487 if (ExpiringBTE == getChildBinding(MTE: Path)) {
488 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<ExpireFact>(
489 args: PL->getID(), args: TemporaryDtor.getBindTemporaryExpr()->getEndLoc()));
490 }
491 }
492 }
493}
494
495void FactsGenerator::handleExitBlock() {
496 // Creates FieldEscapeFacts for all field origins that remain live at exit.
497 for (const Origin &O : FactMgr.getOriginMgr().getOrigins())
498 if (auto *FD = dyn_cast_if_present<FieldDecl>(Val: O.getDecl()))
499 EscapesInCurrentBlock.push_back(
500 Elt: FactMgr.createFact<FieldEscapeFact>(args: O.ID, args&: FD));
501}
502
503void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
504 assert(isGslPointerType(CCE->getType()));
505 if (CCE->getNumArgs() != 1)
506 return;
507
508 const Expr *Arg = CCE->getArg(Arg: 0);
509 if (isGslPointerType(QT: Arg->getType())) {
510 OriginList *ArgList = getOriginsList(E: *Arg);
511 assert(ArgList && "GSL pointer argument should have an origin list");
512 // GSL pointer is constructed from another gsl pointer.
513 // Example:
514 // View(View v);
515 // View(const View &v);
516 ArgList = getRValueOrigins(E: Arg, List: ArgList);
517 flow(Dst: getOriginsList(E: *CCE), Src: ArgList, /*Kill=*/true);
518 } else if (Arg->getType()->isPointerType()) {
519 // GSL pointer is constructed from a raw pointer. Flow only the outermost
520 // raw pointer. Example:
521 // View(const char*);
522 // Span<int*>(const in**);
523 OriginList *ArgList = getOriginsList(E: *Arg);
524 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
525 args: getOriginsList(E: *CCE)->getOuterOriginID(), args: ArgList->getOuterOriginID(),
526 /*Kill=*/args: true));
527 } else {
528 // This could be a new borrow.
529 // TODO: Add code example here.
530 handleFunctionCall(Call: CCE, FD: CCE->getConstructor(),
531 Args: {CCE->getArgs(), CCE->getNumArgs()},
532 /*IsGslConstruction=*/true);
533 }
534}
535
536/// Checks if a call-like expression creates a borrow by passing a value to a
537/// reference parameter, creating an IssueFact if it does.
538/// \param IsGslConstruction True if this is a GSL construction where all
539/// argument origins should flow to the returned origin.
540void FactsGenerator::handleFunctionCall(const Expr *Call,
541 const FunctionDecl *FD,
542 ArrayRef<const Expr *> Args,
543 bool IsGslConstruction) {
544 OriginList *CallList = getOriginsList(E: *Call);
545 // Ignore functions returning values with no origin.
546 FD = getDeclWithMergedLifetimeBoundAttrs(FD);
547 if (!FD || !CallList)
548 return;
549 auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
550 const ParmVarDecl *PVD = nullptr;
551 if (const auto *Method = dyn_cast<CXXMethodDecl>(Val: FD);
552 Method && Method->isInstance()) {
553 if (I == 0)
554 // For the 'this' argument, the attribute is on the method itself.
555 return implicitObjectParamIsLifetimeBound(FD: Method) ||
556 shouldTrackImplicitObjectArg(
557 Callee: Method, /*RunningUnderLifetimeSafety=*/true);
558 if ((I - 1) < Method->getNumParams())
559 // For explicit arguments, find the corresponding parameter
560 // declaration.
561 PVD = Method->getParamDecl(i: I - 1);
562 } else if (I == 0 && shouldTrackFirstArgument(FD)) {
563 return true;
564 } else if (I < FD->getNumParams()) {
565 // For free functions or static methods.
566 PVD = FD->getParamDecl(i: I);
567 }
568 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
569 };
570 auto shouldTrackPointerImplicitObjectArg = [FD](unsigned I) -> bool {
571 const auto *Method = dyn_cast<CXXMethodDecl>(Val: FD);
572 if (!Method || !Method->isInstance())
573 return false;
574 return I == 0 &&
575 isGslPointerType(QT: Method->getFunctionObjectParameterType()) &&
576 shouldTrackImplicitObjectArg(Callee: Method,
577 /*RunningUnderLifetimeSafety=*/true);
578 };
579 if (Args.empty())
580 return;
581 bool KillSrc = true;
582 for (unsigned I = 0; I < Args.size(); ++I) {
583 OriginList *ArgList = getOriginsList(E: *Args[I]);
584 if (!ArgList)
585 continue;
586 if (IsGslConstruction) {
587 // TODO: document with code example.
588 // std::string_view(const std::string_view& from)
589 if (isGslPointerType(QT: Args[I]->getType())) {
590 assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
591 ArgList = getRValueOrigins(E: Args[I], List: ArgList);
592 }
593 if (isGslOwnerType(QT: Args[I]->getType())) {
594 // GSL construction creates a view that borrows from arguments.
595 // This implies flowing origins through the list structure.
596 flow(Dst: CallList, Src: ArgList, Kill: KillSrc);
597 KillSrc = false;
598 }
599 } else if (shouldTrackPointerImplicitObjectArg(I)) {
600 assert(ArgList->getLength() >= 2 &&
601 "Object arg of pointer type should have atleast two origins");
602 // See through the GSLPointer reference to see the pointer's value.
603 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
604 args: CallList->getOuterOriginID(),
605 args: ArgList->peelOuterOrigin()->getOuterOriginID(), args&: KillSrc));
606 KillSrc = false;
607 } else if (IsArgLifetimeBound(I)) {
608 // Lifetimebound on a non-GSL-ctor function means the returned
609 // pointer/reference itself must not outlive the arguments. This
610 // only constraints the top-level origin.
611 CurrentBlockFacts.push_back(Elt: FactMgr.createFact<OriginFlowFact>(
612 args: CallList->getOuterOriginID(), args: ArgList->getOuterOriginID(), args&: KillSrc));
613 KillSrc = false;
614 }
615 }
616}
617
618/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
619/// If so, creates a `TestPointFact` and returns true.
620bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
621 if (!FCE->getType()->isVoidType())
622 return false;
623
624 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
625 if (const auto *SL = dyn_cast<StringLiteral>(Val: SubExpr)) {
626 llvm::StringRef LiteralValue = SL->getString();
627 const std::string Prefix = "__lifetime_test_point_";
628
629 if (LiteralValue.starts_with(Prefix)) {
630 StringRef Annotation = LiteralValue.drop_front(N: Prefix.length());
631 CurrentBlockFacts.push_back(
632 Elt: FactMgr.createFact<TestPointFact>(args&: Annotation));
633 return true;
634 }
635 }
636 return false;
637}
638
639// A DeclRefExpr will be treated as a use of the referenced decl. It will be
640// checked for use-after-free unless it is later marked as being written to
641// (e.g. on the left-hand side of an assignment).
642void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
643 OriginList *List = getOriginsList(E: *DRE);
644 if (!List)
645 return;
646 // Remove the outer layer of origin which borrows from the decl directly
647 // (e.g., when this is not a reference). This is a use of the underlying decl.
648 if (!DRE->getDecl()->getType()->isReferenceType())
649 List = getRValueOrigins(E: DRE, List);
650 // Skip if there is no inner origin (e.g., when it is not a pointer type).
651 if (!List)
652 return;
653 UseFact *UF = FactMgr.createFact<UseFact>(args&: DRE, args&: List);
654 CurrentBlockFacts.push_back(Elt: UF);
655 assert(!UseFacts.contains(DRE));
656 UseFacts[DRE] = UF;
657}
658
659void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
660 if (UseFacts.contains(Val: DRE))
661 UseFacts[DRE]->markAsWritten();
662}
663
664// Creates an IssueFact for a new placeholder loan for each pointer or reference
665// parameter at the function's entry.
666llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
667 const auto *FD = dyn_cast<FunctionDecl>(Val: AC.getDecl());
668 if (!FD)
669 return {};
670
671 llvm::SmallVector<Fact *> PlaceholderLoanFacts;
672 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD); MD && MD->isInstance()) {
673 OriginList *List = *FactMgr.getOriginMgr().getThisOrigins();
674 const PlaceholderLoan *L =
675 FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(args&: MD);
676 PlaceholderLoanFacts.push_back(
677 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
678 }
679 for (const ParmVarDecl *PVD : FD->parameters()) {
680 OriginList *List = getOriginsList(D: *PVD);
681 if (!List)
682 continue;
683 const PlaceholderLoan *L =
684 FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(args&: PVD);
685 PlaceholderLoanFacts.push_back(
686 Elt: FactMgr.createFact<IssueFact>(args: L->getID(), args: List->getOuterOriginID()));
687 }
688 return PlaceholderLoanFacts;
689}
690
691} // namespace clang::lifetimes::internal
692