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