clang 24.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
40bool FactsGenerator::hasOrigins(QualType QT) const {
41 return FactMgr.getOriginMgr().hasOrigins(QT);
42}
43
44bool FactsGenerator::hasOrigins(const Expr *E) const {
45 return FactMgr.getOriginMgr().hasOrigins(E);
46}
47
48/// Propagates origin information from Src to Dst through all levels of
49/// indirection, creating OriginFlowFacts at each level.
50///
51/// This function enforces a critical type-safety invariant: both lists must
52/// have the same shape (same depth/structure). This invariant ensures that
53/// origins flow only between compatible types during expression evaluation.
54///
55/// Examples:
56/// - `int* p = &x;` flows origins from `&x` (depth 1) to `p` (depth 1)
57/// - `int** pp = &p;` flows origins from `&p` (depth 2) to `pp` (depth 2)
58/// * Level 1: pp <- p's address
59/// * Level 2: (*pp) <- what p points to (i.e., &x)
60/// - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
61///
62/// \param Dst The destination origin list.
63/// \param Src The source origin list.
64/// \param Kill If true, the destination's existing loans are killed before
65/// flowing.
66/// \param Block Optional. If provided, the generated flow facts are appended to
67/// this specific CFG block. Otherwise, they are appended to the
68/// current block being visited.
69void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill,
70 const CFGBlock *Block) {
71 if (!Dst)
72 return;
73 assert(Src &&
74 "Dst is non-null but Src is null. List must have the same length");
75 assert(Dst->getLength() == Src->getLength() &&
76 "Lists must have the same length");
77
78 while (Dst && Src) {
79 Fact *F = FactMgr.createFact<OriginFlowFact>(Dst->getOuterOriginID(),
80 Src->getOuterOriginID(), Kill);
81 if (Block)
82 FactMgr.appendBlockFact(Block, F);
83 else
84 CurrentBlockFacts.push_back(F);
85 Dst = Dst->peelOuterOrigin();
86 Src = Src->peelOuterOrigin();
87 }
88}
89
90/// Creates a loan for the storage path of a given declaration.
91/// This function should be called whenever an expression naming \p VD
92/// represents a borrow.
93/// \param VD The declaration whose storage is borrowed.
94/// \param IssueExpr The expression (DeclRefExpr or MemberExpr) that initiates
95/// the borrow.
96/// \return The new Loan on success, nullptr otherwise.
97static const Loan *createLoan(FactManager &FactMgr, const ValueDecl &VD,
98 const Expr *IssueExpr) {
99 return FactMgr.getLoanMgr().createLoan(AccessPath(&VD), IssueExpr);
100}
101
102/// Creates a loan for the storage location of a temporary object.
103/// \param MTE The MaterializeTemporaryExpr that represents the temporary
104/// binding. \return The new Loan.
105static const Loan *createLoan(FactManager &FactMgr,
106 const MaterializeTemporaryExpr *MTE) {
107 AccessPath Path(MTE);
108 return FactMgr.getLoanMgr().createLoan(Path, MTE);
109}
110
111/// Creates a loan for an allocation through 'new'
112/// \param NE The CXXNewExpr that represents the allocation
113/// \return The new Loan on success, nullptr otherwise
114static const Loan *createLoan(FactManager &FactMgr, const CXXNewExpr *NE) {
115 AccessPath Path(NE);
116 return FactMgr.getLoanMgr().createLoan(Path, NE);
117}
118
120 llvm::TimeTraceScope TimeProfile("FactGenerator");
121 const CFG &Cfg = *AC.getCFG();
122 llvm::SmallVector<Fact *> PlaceholderLoanFacts = issuePlaceholderLoans();
123 // Iterate through the CFG blocks in reverse post-order to ensure that
124 // initializations and destructions are processed in the correct sequence.
125 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
126 CurrentBlockFacts.clear();
127 EscapesInCurrentBlock.clear();
128 CurrentBlock = Block;
129 if (Block == &Cfg.getEntry())
130 CurrentBlockFacts.append(PlaceholderLoanFacts.begin(),
131 PlaceholderLoanFacts.end());
132 for (unsigned I = 0; I < Block->size(); ++I) {
133 const CFGElement &Element = Block->Elements[I];
134 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
135 Visit(CS->getStmt());
136 else if (std::optional<CFGInitializer> Initializer =
137 Element.getAs<CFGInitializer>())
138 handleCXXCtorInitializer(Initializer->getInitializer());
139 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
140 Element.getAs<CFGLifetimeEnds>())
141 handleLifetimeEnds(*LifetimeEnds);
142 else if (std::optional<CFGFullExprCleanup> FullExprCleanup =
143 Element.getAs<CFGFullExprCleanup>()) {
144 handleFullExprCleanup(*FullExprCleanup);
145 }
146 }
147 if (Block == &Cfg.getExit())
148 handleExitBlock();
149
150 CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
151 EscapesInCurrentBlock.end());
152 FactMgr.addBlockFacts(Block, CurrentBlockFacts);
153 }
154 FactMgr.computePersistentOrigins(Cfg);
155}
156
157OriginList *FactsGenerator::readValue(const Expr *E) {
158 OriginList *List = getOriginsList(*E);
159 if (!List)
160 return nullptr;
161 if (!E->isGLValue())
162 return List;
163 handleAccess(E);
164 return List->peelOuterOrigin();
165}
166
168 for (const Decl *D : DS->decls())
169 if (const auto *VD = dyn_cast<VarDecl>(D))
170 if (const Expr *InitExpr = VD->getInit()) {
171 OriginList *VDList = getOriginsList(*VD);
172 if (!VDList)
173 continue;
174 OriginList *InitList = getOriginsList(*InitExpr);
175 assert(InitList && "VarDecl had origins but InitExpr did not");
176 flow(VDList, InitList, /*Kill=*/true);
177 }
178}
179
181 // Skip function references as their lifetimes are not interesting. Skip non
182 // GLValues (like EnumConstants).
183 if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())
184 return;
185 // For all declarations with storage (non-references), we issue a loan
186 // representing the borrow of the variable's storage itself.
187 //
188 // Examples:
189 // - `int x; x` issues loan to x's storage
190 // - `int* p; p` issues loan to p's storage (the pointer variable)
191 // - `View v; v` issues loan to v's storage (the view object)
192 // - `int& r = x; r` issues no loan (r has no storage, it's an alias to x)
193 if (doesDeclHaveStorage(DRE->getDecl())) {
194 const Loan *L = createLoan(FactMgr, *DRE->getDecl(), DRE);
195 assert(L);
196 OriginList *List = getOriginsList(*DRE);
197 assert(List &&
198 "gl-value DRE of non-pointer type should have an origin list");
199 // This loan specifically tracks borrowing the variable's storage location
200 // itself and is issued to outermost origin (List->OID).
201 CurrentBlockFacts.push_back(
202 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
203 }
204}
205
207 if (isGslPointerType(CCE->getType())) {
208 handleGSLPointerConstruction(CCE);
209 return;
210 }
211 // For defaulted (implicit or `= default`) copy/move constructors, propagate
212 // origins directly. User-defined copy/move constructors are not handled here
213 // as they have opaque semantics.
215 CCE->getConstructor()->isDefaulted() && CCE->getNumArgs() == 1 &&
216 hasOrigins(CCE->getType())) {
217 const Expr *Arg = CCE->getArg(0);
218 if (OriginList *ArgList = readValue(Arg)) {
219 flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
220 return;
221 }
222 }
223 // Standard library callable wrappers (e.g., std::function) propagate the
224 // stored lambda's origins.
225 if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
226 RD && isStdCallableWrapperType(RD) && CCE->getNumArgs() == 1) {
227 const Expr *Arg = CCE->getArg(0);
228 if (OriginList *ArgList = readValue(Arg)) {
229 flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
230 return;
231 }
232 }
233 handleFunctionCall(CCE, /*IsGslConstruction=*/false);
234}
235
237 if (const Expr *Init = DIE->getExpr())
238 killAndFlowOrigin(*DIE, *Init);
239}
240
241void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) {
242 // Flows origins from the initializer expression to the field.
243 // Example: `MyObj(std::string s) : view(s) {}`
244 if (const FieldDecl *FD = CII->getAnyMember())
245 killAndFlowOrigin(*FD, *CII->getInit());
246}
247
249 // Specifically for conversion operators,
250 // like `std::string_view p = std::string{};`
251 if (isGslPointerType(MCE->getType()) &&
252 isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl()) &&
254 handleFunctionCall(MCE, /*IsGslConstruction=*/true);
255 return;
256 }
257 handleFunctionCall(MCE, /*IsGslConstruction=*/false);
258}
259
261 auto *MD = ME->getMemberDecl();
262 if (const auto *VD = dyn_cast<VarDecl>(MD); VD && doesDeclHaveStorage(VD)) {
263 // A static data member: borrow its storage, as for a DeclRefExpr.
264 const Loan *L = createLoan(FactMgr, *VD, ME);
265 OriginList *List = getOriginsList(*ME);
266 assert(List && "gl-value static member should have an origin list");
267 CurrentBlockFacts.push_back(
268 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
269 return;
270 }
271 if (isa<FieldDecl>(MD) && doesDeclHaveStorage(MD)) {
272 assert(ME->isGLValue() && "Field member should be GL value");
273 OriginList *Dst = getOriginsList(*ME);
274 assert(Dst && "Field member should have an origin list as it is GL value");
275 OriginList *Src = getOriginsList(*ME->getBase());
276 assert(Src && "Base expression should be a pointer/reference type");
277 // The field's glvalue (outermost origin) holds the same loans as the base
278 // expression.
279 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
280 Dst->getOuterOriginID(), Src->getOuterOriginID(),
281 /*Kill=*/true));
282 }
283}
284
286 handleFunctionCall(CE);
287}
288
290 const CXXNullPtrLiteralExpr *N) {
291 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
292 /// pointers can use the same type of loan.
293 getOriginsList(*N);
294}
295
297 const Expr *SubExpr = CE->getSubExpr();
298 // May be null: loading an `int` has no origins but still reads the operand.
299 OriginList *Dest = getOriginsList(*CE);
300 OriginList *Src = Dest ? getOriginsList(*SubExpr) : nullptr;
301
302 switch (CE->getCastKind()) {
303 case CK_LValueToRValue:
304 // The result of an LValue-to-RValue cast on a pointer lvalue (like `q` in
305 // `int *p, *q; p = q;`) should propagate the inner origin (what the pointer
306 // points to), not the outer origin (the pointer's storage location).
307 flow(Dest, readValue(SubExpr), /*Kill=*/true);
308 return;
309 case CK_Dynamic:
310 handleAccess(SubExpr);
311 return;
312 case CK_NullToPointer:
313 // TODO: Flow into them a null origin.
314 return;
315 case CK_NoOp:
316 case CK_ConstructorConversion:
317 case CK_UserDefinedConversion:
318 flow(Dest, Src, /*Kill=*/true);
319 return;
320 case CK_UncheckedDerivedToBase:
321 case CK_DerivedToBase:
322 case CK_BaseToDerived:
323 // The conversion cast doesn't change what it refers to, so preserve the
324 // borrow if we can.
325 // It is possible that the derived class and base class have different
326 // gsl::Pointer annotations. Skip if their origin shape differ.
327 if (Dest && Src && Dest->getLength() == Src->getLength())
328 flow(Dest, Src, /*Kill=*/true);
329 return;
330 case CK_ArrayToPointerDecay:
331 // va_arg(ap, array_type) is UB and does not provide addressable array
332 // storage to model.
333 if (!Dest || isa<VAArgExpr>(SubExpr->IgnoreParens()))
334 return;
335 assert(Src && "Array expression should have origins as it is GL value");
336 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
337 Dest->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true));
338 return;
339 case CK_FunctionToPointerDecay:
340 case CK_BuiltinFnToFnPtr:
341 // Ignore function-to-pointer decays.
342 return;
343 case CK_BitCast:
344 // OriginLists for Src and Dst may differ here. For example when casting
345 // from int** to void*
346 if (Src && Dest && Dest->getLength() == Src->getLength())
347 flow(Dest, Src, /*Kill=*/true);
348 return;
349 case CK_LValueToRValueBitCast:
350 case CK_NonAtomicToAtomic:
351 case CK_AtomicToNonAtomic: {
352 // `__builtin_bit_cast`/`std::bit_cast` of a pointer, and
353 // wrapping/unwrapping `_Atomic(T*)`, preserve the pointer value, so
354 // propagate the borrow. readValue peels a glvalue operand's storage level
355 // and records the read. A bit-cast that materializes a pointer from a
356 // non-pointer representation has no matching source origin and is
357 // untracked.
358 OriginList *RVSrc = readValue(SubExpr);
359 if (Dest && RVSrc && Dest->getLength() == RVSrc->getLength())
360 flow(Dest, RVSrc, /*Kill=*/true);
361 return;
362 }
363 default:
364 return;
365 }
366}
367
369 switch (UO->getOpcode()) {
370 case UO_AddrOf: {
371 const Expr *SubExpr = UO->getSubExpr();
372 // Function addresses do not need lifetime tracking.
373 if (SubExpr->getType()->isFunctionType())
374 return;
375 // Skip address-of on void expressions: GNU C permits them, but void itself
376 // has no origins to track.
377 if (IsCMode && SubExpr->getType()->isVoidType())
378 return;
379 assert(!SubExpr->getType()->isVoidType() &&
380 "Taking address of void is not valid in C++");
381 // The origin of an address-of expression (e.g., &x) is the origin of
382 // its sub-expression (x). This fact will cause the dataflow analysis
383 // to propagate any loans held by the sub-expression's origin to the
384 // origin of this UnaryOperator expression.
385 killAndFlowOrigin(*UO, *SubExpr);
386 return;
387 }
388 case UO_Deref: {
389 const Expr *SubExpr = UO->getSubExpr();
390 killAndFlowOrigin(*UO, *SubExpr);
391 return;
392 }
393 case UO_Plus: {
394 // Unary plus on a pointer is the identity (`+p == p`), so the prvalue
395 // result carries the operand's loans. Flow the operand's rvalue origins
396 // (peeling storage only when the operand is itself a glvalue).
397 if (!UO->getType()->isPointerType())
398 return;
399 const Expr *SubExpr = UO->getSubExpr();
400 flow(getOriginsList(*UO), readValue(SubExpr), /*Kill=*/true);
401 return;
402 }
403 case UO_PreInc:
404 case UO_PostInc:
405 case UO_PreDec:
406 case UO_PostDec: {
407 handleAccess(UO->getSubExpr());
408 // Inc/dec keeps a pointer in the same allocation, so the result carries the
409 // operand's loans. Peel the operand's storage origin when the *result* is a
410 // prvalue (post-inc/dec, or any form in C).
411 if (!UO->getType()->isPointerType())
412 return;
413 OriginList *SubList = getOriginsList(*UO->getSubExpr());
414 flow(getOriginsList(*UO),
415 UO->isGLValue() ? SubList : SubList->peelOuterOrigin(), /*Kill=*/true);
416 return;
417 }
418 default:
419 return;
420 }
421}
422
424 if (const Expr *RetExpr = RS->getRetValue()) {
425 if (OriginList *List = getOriginsList(*RetExpr))
426 for (OriginList *L = List; L != nullptr; L = L->peelOuterOrigin())
427 EscapesInCurrentBlock.push_back(FactMgr.createFact<ReturnEscapeFact>(
428 L->getOuterOriginID(), RetExpr));
429 }
430}
431
432void FactsGenerator::handleAssignment(const Expr *TargetExpr,
433 const Expr *LHSExpr,
434 const Expr *RHSExpr) {
435 LHSExpr = LHSExpr->IgnoreParenImpCasts();
436 handleAccess(LHSExpr);
437 OriginList *RHSList = readValue(RHSExpr);
438 OriginList *LHSList = nullptr;
439
440 if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) {
441 LHSList = getOriginsList(*DRE_LHS);
442 assert(LHSList && "LHS is a DRE and should have an origin list");
443 }
444 // Handle assignment to member fields (e.g., `this->view = s` or `view = s`).
445 // This enables detection of dangling fields when local values escape to
446 // fields.
447 if (const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) {
448 LHSList = getOriginsList(*ME_LHS);
449 assert(LHSList && "LHS is a MemberExpr and should have an origin list");
450 }
451 if (!LHSList)
452 return;
453
454 if (!RHSList) {
455 // RHS has no tracked origins (e.g., assigning a callable without origins
456 // to std::function). Clear loans of the destination.
457 for (OriginList *LHSInner = LHSList->peelOuterOrigin(); LHSInner;
458 LHSInner = LHSInner->peelOuterOrigin())
459 CurrentBlockFacts.push_back(
460 FactMgr.createFact<KillOriginFact>(LHSInner->getOuterOriginID()));
461 return;
462 }
463 // Kill the old loans of the destination origin and flow the new loans
464 // from the source origin.
465 flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
466
467 // In C, assignment expressions are not GLValues, so the assignment result has
468 // the assigned value origins, not the LHS storage origin.
469 if (IsCMode)
470 LHSList = LHSList->peelOuterOrigin();
471 flow(getOriginsList(*TargetExpr), LHSList, /*Kill=*/true);
472}
473
474void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
475 if (Expr *RHS = BO->getRHS(); RHS->getType()->isPointerType()) {
476 killAndFlowOrigin(*BO, *RHS);
477 return;
478 }
479 Expr *LHS = BO->getLHS();
480 assert(LHS->getType()->isPointerType() &&
481 "Pointer arithmetic must have a pointer operand");
482 killAndFlowOrigin(*BO, *LHS);
483}
484
486 if (BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI) {
487 // `obj.*pm` / `objptr->*pm` names a member of the object, so a borrow of it
488 // borrows the object; flow the object's origin into the result. For `.*`
489 // the object is the LHS; for `->*` it is the LHS pointer's pointee.
490 //
491 // Only the result's outer (storage) origin relates to the object: borrowing
492 // the member borrows the object's storage. Deeper levels of the result (a
493 // pointer/view member's own pointee) are the member's value, with no
494 // counterpart in the object's origin -- so the lists may differ in length
495 // and we flow just the top level, leaving the member's value untouched.
496 OriginList *Dst = getOriginsList(*BO);
497 OriginList *ObjSrc = BO->getOpcode() == BO_PtrMemD
498 ? getOriginsList(*BO->getLHS())
499 : readValue(BO->getLHS());
500 if (Dst && ObjSrc)
501 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
502 Dst->getOuterOriginID(), ObjSrc->getOuterOriginID(), /*Kill=*/true));
503 return;
504 }
505 if (BO->getOpcode() == BO_Comma) {
506 killAndFlowOrigin(*BO, *BO->getRHS());
507 return;
508 }
509 if (BO->isCompoundAssignmentOp()) {
510 handleAccess(BO->getLHS());
511 // A pointer compound additive assignment (`p += n`) carries the LHS's loans
512 // like inc/dec above; in C the result is a prvalue, so peel its outer
513 // (storage) origin.
514 if (BO->getType()->isPointerType()) {
515 OriginList *LHSList = getOriginsList(*BO->getLHS());
516 flow(getOriginsList(*BO), IsCMode ? LHSList->peelOuterOrigin() : LHSList,
517 /*Kill=*/true);
518 }
519 return;
520 }
521 if (BO->getType()->isPointerType() && BO->isAdditiveOp())
522 handlePointerArithmetic(BO);
523 if (BO->isAssignmentOp())
524 handleAssignment(BO, BO->getLHS(), BO->getRHS());
525 // TODO: Propagate origins for assignments through a dereference (`*p = q`).
526}
527
528static const CFGBlock *findPredBlockForExpr(const CFGBlock *MergeBlock,
529 const Expr *ArmExpr) {
530 if (!ArmExpr)
531 return nullptr;
532 const Expr *Target = ArmExpr->IgnoreParenImpCasts();
533 if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Target))
534 if (const Expr *Src = OVE->getSourceExpr())
535 Target = Src->IgnoreParenImpCasts();
536
537 for (const CFGBlock *Pred : MergeBlock->preds()) {
538 if (!Pred)
539 continue;
540 for (const CFGElement &Elt : *Pred)
541 if (auto CS = Elt.getAs<CFGStmt>())
542 if (const auto *E = dyn_cast<Expr>(CS->getStmt()))
543 if (E->IgnoreParenImpCasts() == Target)
544 return Pred;
545 }
546 return nullptr;
547}
548
549/// Visits conditional operators (e.g., `cond ? a : b`).
550///
551/// To prevent liveness leakage across loop backedges (which causes false
552/// positives like in `while (...) { int x; consume(cond ? &x : nullptr); }`),
553/// we generate the flow facts in the respective predecessor blocks of the arms
554/// rather than in the merge block. This ensures that the liveness of the
555/// temporary origin from one arm does not propagate into the other arm's path.
557 const AbstractConditionalOperator *CO) {
558 if (!hasOrigins(CO))
559 return;
560
561 const Expr *TrueExpr = CO->getTrueExpr();
562 const Expr *FalseExpr = CO->getFalseExpr();
563
564 if (const CFGBlock *TBPred = findPredBlockForExpr(CurrentBlock, TrueExpr))
565 flow(getOriginsList(*CO), getOriginsList(*TrueExpr), /*Kill=*/true, TBPred);
566 if (const CFGBlock *FBPred = findPredBlockForExpr(CurrentBlock, FalseExpr))
567 flow(getOriginsList(*CO), getOriginsList(*FalseExpr), /*Kill=*/true,
568 FBPred);
569}
570
572 // Assignment operators have special "kill-then-propagate" semantics
573 // and are handled separately.
574 if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2 &&
575 hasOrigins(OCE->getArg(0)->getType())) {
576 // Pointer-like types: assignment inherently propagates origins.
577 QualType LHSTy = OCE->getArg(0)->getType();
578 if (LHSTy->isPointerOrReferenceType() || isGslPointerType(LHSTy) ||
579 isGslOwnerType(LHSTy)) {
580 handleAssignment(OCE, OCE->getArg(0), OCE->getArg(1));
581 return;
582 }
583 // Standard library callable wrappers (e.g., std::function) can propagate
584 // the stored lambda's origins.
585 if (const auto *RD = LHSTy->getAsCXXRecordDecl();
586 RD && isStdCallableWrapperType(RD)) {
587 handleAssignment(OCE, OCE->getArg(0), OCE->getArg(1));
588 return;
589 }
590 // Other tracked types: only defaulted operator= propagates origins.
591 // User-defined operator= has opaque semantics, so don't handle them now.
592 if (const auto *MD =
593 dyn_cast_or_null<CXXMethodDecl>(OCE->getDirectCallee());
594 MD && MD->isDefaulted()) {
595 handleAssignment(OCE, OCE->getArg(0), OCE->getArg(1));
596 return;
597 }
598 }
599
600 handleFunctionCall(OCE);
601}
602
604 const CXXFunctionalCastExpr *FCE) {
605 // Check if this is a test point marker. If so, we are done with this
606 // expression.
607 if (handleTestPoint(FCE))
608 return;
609 VisitCastExpr(FCE);
610}
611
613 if (!hasOrigins(ILE))
614 return;
615 // For list initialization with a single element, like `View{...}`, the
616 // origin of the list itself is the origin of its single element.
617 if (ILE->getNumInits() == 1) {
618 // A type with origins may be list-initialized from an element with none
619 // (e.g., an int). Only flow if the element carries any.
620 if (!hasOrigins(ILE->getInit(0)))
621 return;
622 killAndFlowOrigin(*ILE, *ILE->getInit(0));
623 }
624}
625
627 const CXXBindTemporaryExpr *BTE) {
628 killAndFlowOrigin(*BTE, *BTE->getSubExpr());
629}
630
632 const MaterializeTemporaryExpr *MTE) {
633 assert(MTE->isGLValue());
634 OriginList *MTEList = getOriginsList(*MTE);
635 if (!MTEList)
636 return;
637 OriginList *SubExprList = getOriginsList(*MTE->getSubExpr());
638 assert((!SubExprList ||
639 MTEList->getLength() == (SubExprList->getLength() + 1)) &&
640 "MTE top level origin should contain a loan to the MTE itself");
641
642 OriginList *RValMTEList = MTEList->peelOuterOrigin();
643 flow(RValMTEList, SubExprList, /*Kill=*/true);
644 OriginID OuterMTEID = MTEList->getOuterOriginID();
645 const Loan *L = createLoan(FactMgr, MTE);
646 CurrentBlockFacts.push_back(
647 FactMgr.createFact<IssueFact>(L->getID(), OuterMTEID));
648 // A temporary extended to static or thread storage duration does not expire
649 // within the function.
650 // Thread termination is not modeled; a thread-local value used after its
651 // thread exits is not diagnosed.
652 if (MTE->getStorageDuration() == SD_Automatic)
653 if (const ValueDecl *ExtendingDecl = MTE->getExtendingDecl())
654 ExtendedTemporaries[ExtendingDecl].push_back(MTE);
655}
656
658 for (const LambdaCapture &C : LE->captures()) {
659 if (C.capturesThis())
660 FactMgr.setThisCapturedByLambda();
661 else if (C.capturesVariable() && C.getCapturedVar()->isInitCapture()) {
662 const Expr *Init = cast<VarDecl>(C.getCapturedVar())->getInit();
663 if (!Init)
664 continue;
665 if (const auto *ME = dyn_cast<MemberExpr>(Init->IgnoreParenImpCasts())) {
666 if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
667 FactMgr.addCapturedField(FD);
668 }
669 }
670 }
671
672 // The lambda gets a single merged origin that aggregates all captured
673 // pointer-like origins. Currently we only need to detect whether the lambda
674 // outlives any capture.
675 OriginList *LambdaList = getOriginsList(*LE);
676 if (!LambdaList)
677 return;
678 bool Kill = true;
679 for (const Expr *Init : LE->capture_inits()) {
680 if (!Init)
681 continue;
682 // The lambda body may dereference a capture to any depth.
683 handleUse(Init);
684 OriginList *InitList = getOriginsList(*Init);
685 if (!InitList)
686 continue;
687 // FIXME: Consider flowing all origin levels once lambdas support more than
688 // one origin. Currently only the outermost origin is flowed, so by-ref
689 // captures like `[&p]` (where p is string_view) miss inner-level
690 // invalidation.
691 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
692 LambdaList->getOuterOriginID(), InitList->getOuterOriginID(), Kill));
693 Kill = false;
694 }
695}
696
698 // Some C subscripts do not refer to addressable storage with origins, such as
699 // GNU void-pointer subscripts and vector element extraction from rvalues.
700 if (IsCMode && !ASE->isGLValue())
701 return;
702 assert(ASE->isGLValue() && "Array subscript should be a GL value");
703 OriginList *Dst = getOriginsList(*ASE);
704 assert(Dst && "Array subscript should have origins as it is a GL value");
705 OriginList *Src = getOriginsList(*ASE->getBase());
706 assert(Src && "Base of array subscript should have origins");
707 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
708 Dst->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true));
709}
710
711bool FactsGenerator::handlePlacementNew(const CXXNewExpr *NE,
712 OriginList *NewList) {
713 // Model only the standard single-argument placement new form, where the
714 // placement argument corresponds to a void* allocation-function parameter.
715 // Other placement forms, such as std::nothrow, are not modeled as providing
716 // storage for the returned pointer.
717 if (NE->getNumPlacementArgs() != 1)
718 return false;
719
720 const FunctionDecl *OperatorNew = NE->getOperatorNew();
721 if (OperatorNew->getNumParams() <= 1)
722 return false;
723
724 const auto *Arg =
725 OperatorNew->getParamDecl(1)->getType()->getAs<PointerType>();
726 if (!Arg || !Arg->isVoidPointerType())
727 return false;
728
729 // Use the placement argument before the implicit conversion to void*, so
730 // inner origins are still available.
731 const Expr *PlacementArg = NE->getPlacementArg(0);
732 if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg);
733 ICE && ICE->getCastKind() == CK_BitCast &&
734 PlacementArg->getType()->isVoidPointerType())
735 PlacementArg = ICE->getSubExpr();
736 OriginList *PlacementList = getOriginsList(*PlacementArg);
737 // FIXME: General placement arguments need separate handling to overwrite
738 // the right origins.
739
740 handleAccess(PlacementArg);
741
742 // The pointer returned by placement new comes from the placement
743 // argument.
744 if (PlacementList)
745 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
746 NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), true));
747 return true;
748}
749
751 OriginList *NewList = getOriginsList(*NE);
752 const Expr *Init = NE->getInitializer();
753
754 bool HandledAsPlacementNew = false;
755 if (NE->getNumPlacementArgs() == 1)
756 HandledAsPlacementNew = handlePlacementNew(NE, NewList);
757
758 // Treat ordinary new and replaceable global allocation forms as heap
759 // allocations.
760 const FunctionDecl *OperatorNew = NE->getOperatorNew();
761 if (!HandledAsPlacementNew &&
762 (NE->getNumPlacementArgs() == 0 ||
763 (OperatorNew && OperatorNew->isReplaceableGlobalAllocationFunction()))) {
764 const Loan *L = createLoan(FactMgr, NE);
765 CurrentBlockFacts.push_back(
766 FactMgr.createFact<IssueFact>(L->getID(), NewList->getOuterOriginID()));
767 }
768
769 NewList = NewList->peelOuterOrigin();
770
771 if (!NewList || !Init)
772 return;
773
774 // FIXME: OriginList is null for `new[]` initializers. Remove this `Init`
775 // check once array origins are supported.
776 if (OriginList *InitList = getOriginsList(*Init); InitList)
777 flow(NewList, InitList, true);
778}
779
780// TODO: An escape fact may fit `throw` and asm better than a use.
782 if (const Expr *Sub = TE->getSubExpr())
783 handleUse(Sub);
784}
785
787 for (const Expr *Input : AS->inputs())
788 handleUse(Input);
789 for (unsigned I = 0, N = AS->getNumOutputs(); I != N; ++I)
790 if (AS->isOutputPlusConstraint(I)) // Also read.
791 handleUse(AS->getOutputExpr(I));
792 else
793 handleAccess(AS->getOutputExpr(I));
794}
795
797 if (TE->isPotentiallyEvaluated())
798 handleAccess(TE->getExprOperand());
799}
800
802 // The destructor may dereference to any depth.
803 handleUse(DE->getArgument());
804 OriginList *List = getOriginsList(*DE->getArgument());
805 CurrentBlockFacts.push_back(
806 FactMgr.createFact<InvalidateOriginFact>(List->getOuterOriginID(), DE));
807}
808
810 // A statement expression (`({ ...; e; })`) yields the value of its final
811 // expression `e`. Flow `e`'s origins into the statement expression's origin
812 // so a borrow `e` carries reaches the value's users.
813 const auto *CS = SE->getSubStmt();
814 if (!CS || CS->body_empty())
815 return;
816 const auto *Last = dyn_cast<Expr>(CS->body_back());
817 if (!Last)
818 return;
819 if (OriginList *Dst = getOriginsList(*SE))
820 if (OriginList *Src = readValue(Last))
821 flow(Dst, Src, /*Kill=*/true);
822}
823
824bool FactsGenerator::escapesViaReturn(OriginID OID) const {
825 return llvm::any_of(EscapesInCurrentBlock, [OID](const Fact *F) {
826 if (const auto *EF = F->getAs<ReturnEscapeFact>())
827 return EF->getEscapedOriginID() == OID;
828 return false;
829 });
830}
831
832void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
833 const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
834 if (!LifetimeEndsVD)
835 return;
836 SourceLocation EndLoc = LifetimeEnds.getTriggerStmt()->getEndLoc();
837 // A cleanup callback or a non-trivial destructor (other than an owner's) run
838 // at scope exit may read the borrows the variable holds.
839 QualType Ty = LifetimeEndsVD->getType();
840 bool HasCleanup = LifetimeEndsVD->hasAttr<CleanupAttr>();
841 bool IsDestructorUse =
842 LSOpts.CheckDestructorUses &&
845 if (HasCleanup || IsDestructorUse)
846 if (OriginList *List = getOriginsList(*LifetimeEndsVD))
847 CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(
848 UseSite::scopeExit(EndLoc, *LifetimeEndsVD,
849 HasCleanup ? UseKind::Cleanup
851 List));
852 // Expire the origin when its variable's lifetime ends to ensure liveness
853 // doesn't persist through loop back-edges.
854 std::optional<OriginID> ExpiredOID;
855 if (OriginList *List = getOriginsList(*LifetimeEndsVD)) {
856 OriginID OID = List->getOuterOriginID();
857 // Skip origins that escape via return; the escape checker needs their loans
858 // to remain until the return statement is processed.
859 if (!escapesViaReturn(OID))
860 ExpiredOID = OID;
861 }
862 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
863 AccessPath(LifetimeEndsVD), EndLoc, ExpiredOID));
864 if (auto It = ExtendedTemporaries.find(LifetimeEndsVD);
865 It != ExtendedTemporaries.end())
866 for (const MaterializeTemporaryExpr *MTE : It->second)
867 CurrentBlockFacts.push_back(
868 FactMgr.createFact<ExpireFact>(AccessPath(MTE), EndLoc));
869}
870
871void FactsGenerator::handleFullExprCleanup(
872 const CFGFullExprCleanup &FullExprCleanup) {
873 for (const auto *MTE : FullExprCleanup.getExpiringMTEs())
874 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
875 AccessPath(MTE), FullExprCleanup.getCleanupLoc()));
876}
877
878void FactsGenerator::handleExitBlock() {
879 bool IsDestructor = isa_and_nonnull<CXXDestructorDecl>(AC.getDecl());
880 for (const Origin &O : FactMgr.getOriginMgr().getOrigins())
881 // Create FieldEscapeFacts for all field origins that remain live at exit.
882 // Fields in destructors do not escape since the object is being destroyed.
883 if (auto *FD = dyn_cast_if_present<FieldDecl>(O.getDecl());
884 FD && !IsDestructor)
885 EscapesInCurrentBlock.push_back(
886 FactMgr.createFact<FieldEscapeFact>(O.ID, FD));
887 else if (auto *VD = dyn_cast_if_present<VarDecl>(O.getDecl())) {
888 // Create GlobalEscapeFacts for all origins with global-storage that
889 // remain live at exit.
890 if (VD->hasGlobalStorage()) {
891 EscapesInCurrentBlock.push_back(
892 FactMgr.createFact<GlobalEscapeFact>(O.ID, VD));
893 }
894 }
895
896 // Keep the never-expiring `this` origin live at exit: the object outlives
897 // the method, so whatever it holds (e.g. borrows captured via
898 // [[clang::lifetime_capture_by_this]], or loans reachable through its
899 // fields) is considered used after the method returns. This is emitted for
900 // every method with a `this` origin, not only those that capture something.
901 // Destructors are skipped since the object is being destroyed.
902 if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins();
903 ThisOrigins && !IsDestructor)
904 EscapesInCurrentBlock.push_back(FactMgr.createFact<ThisEscapeFact>(
905 (*ThisOrigins)->getOuterOriginID(), AC.getDecl()->getEndLoc()));
906}
907
908void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
909 assert(isGslPointerType(CCE->getType()));
910 if (CCE->getNumArgs() != 1)
911 return;
912
913 const Expr *Arg = CCE->getArg(0);
914 if (isGslPointerType(Arg->getType())) {
915 // GSL pointer is constructed from another gsl pointer.
916 // Example:
917 // View(View v);
918 // View(const View &v);
919 OriginList *ArgList = readValue(Arg);
920 assert(ArgList && "GSL pointer argument should have an origin list");
921 flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
922 } else if (Arg->getType()->isPointerType()) {
923 // GSL pointer is constructed from a raw pointer. Flow only the outermost
924 // raw pointer. Example:
925 // View(const char*);
926 // Span<int*>(const in**);
927 OriginList *ArgList = getOriginsList(*Arg);
928 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
929 getOriginsList(*CCE)->getOuterOriginID(), ArgList->getOuterOriginID(),
930 /*Kill=*/true));
931 } else {
932 // This could be a new borrow.
933 // TODO: Add code example here.
934 handleFunctionCall(CCE, /*IsGslConstruction=*/true);
935 }
936}
937
938void FactsGenerator::handleMovedArgsInCall(const FunctionDecl *FD,
939 ArrayRef<const Expr *> Args) {
940 unsigned ImplicitObjectArgOffset = 0;
941 // Constructors are excluded because Args has no object argument for them,
942 // even though isImplicitObjectMemberFunction() is true.
943 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
944 MD && !isa<CXXConstructorDecl>(FD) &&
945 MD->isImplicitObjectMemberFunction()) {
946 ImplicitObjectArgOffset = 1;
947 // std::unique_ptr::release() transfers ownership.
948 // Treat it as a move to prevent false-positive warnings when the unique_ptr
949 // destructor runs after ownership has been transferred.
950 if (isUniquePtrRelease(*MD)) {
951 const Expr *UniquePtrExpr = Args[0];
952 OriginList *MovedOrigins = getOriginsList(*UniquePtrExpr);
953 if (MovedOrigins)
954 CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>(
955 UniquePtrExpr, MovedOrigins->getOuterOriginID()));
956 }
957 }
958
959 // Skip implicit 'this' arg as it cannot be moved.
960 for (unsigned I = ImplicitObjectArgOffset;
961 I < Args.size() && I < FD->getNumParams() + ImplicitObjectArgOffset;
962 ++I) {
963 const ParmVarDecl *PVD = FD->getParamDecl(I - ImplicitObjectArgOffset);
964 // In principle, explicit object parameters can be moved, but skip marking
965 // them as moved for consistency with implicit 'this'.
966 if (PVD->isExplicitObjectParameter())
967 continue;
968 if (!PVD->getType()->isRValueReferenceType())
969 continue;
970 // Skip lifetime annotated r-value reference parameters. Lifetime annotation
971 // indicates that the parameter is borrowed (not consumed), so it should not
972 // be marked as moved even though it's an r-value reference.
973 if (PVD->hasAttr<LifetimeBoundAttr>() ||
974 PVD->hasAttr<LifetimeCaptureByAttr>())
975 continue;
976 const Expr *Arg = Args[I];
977 OriginList *MovedOrigins = getOriginsList(*Arg);
978 assert(MovedOrigins->getLength() >= 1 &&
979 "unexpected length for r-value reference param");
980 // Arg is being moved to this parameter. Mark the origin as moved.
981 CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>(
982 Arg, MovedOrigins->getOuterOriginID()));
983 }
984}
985
986void FactsGenerator::handleInvalidatingCall(const Expr *Call,
987 const FunctionDecl *FD,
988 ArrayRef<const Expr *> Args) {
989 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
990 if (!MD || !MD->isInstance())
991 return;
992
993 if (!isInvalidationMethod(*MD))
994 return;
995
996 // Heuristics to turn-down false positives. Skip member field expressions for
997 // now. This is not a perfect filter and will still surface some false
998 // positives (e.g. `auto& r = s.v`). Static data members are not fields.
999 const Expr *Obj = Args[0]->IgnoreImpCasts();
1000 if (const auto *ME = dyn_cast<MemberExpr>(Obj);
1001 !isa<DeclRefExpr>(Obj) && !(ME && isa<VarDecl>(ME->getMemberDecl())))
1002 return;
1003
1004 OriginList *ThisList = getOriginsList(*Args[0]);
1005 if (ThisList)
1006 CurrentBlockFacts.push_back(FactMgr.createFact<InvalidateOriginFact>(
1007 ThisList->getOuterOriginID(), Call));
1008}
1009
1010void FactsGenerator::handleDestructiveCall(const Expr *Call,
1011 const FunctionDecl *FD,
1012 ArrayRef<const Expr *> Args) {
1013 if (!destructsFirstArg(*FD))
1014 return;
1015 OriginList *ArgList = getOriginsList(*Args[0]);
1016 if (ArgList)
1017 CurrentBlockFacts.push_back(FactMgr.createFact<InvalidateOriginFact>(
1018 ArgList->getOuterOriginID(), Call));
1019}
1020
1021void FactsGenerator::handleImplicitObjectFieldUses(const Expr *Call,
1022 const FunctionDecl *FD) {
1023 const auto *MemberCall = dyn_cast_or_null<CXXMemberCallExpr>(Call);
1024 if (!MemberCall)
1025 return;
1026
1027 if (!isa_and_present<CXXThisExpr>(
1028 MemberCall->getImplicitObjectArgument()->IgnoreImpCasts()))
1029 return;
1030
1031 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
1032 assert(MD && "Function must be a CXXMethodDecl for member calls");
1033
1034 const auto *ClassDecl = MD->getParent()->getDefinition();
1035 if (!ClassDecl)
1036 return;
1037
1038 const auto UseFields = [&](const CXXRecordDecl *RD) {
1039 for (const auto *Field : RD->fields())
1040 if (auto *FieldList = getOriginsList(*Field))
1041 CurrentBlockFacts.push_back(
1042 FactMgr.createFact<UseFact>(*Call, FieldList));
1043 };
1044
1045 UseFields(ClassDecl);
1046
1047 ClassDecl->forallBases([&](const CXXRecordDecl *Base) {
1048 UseFields(Base);
1049 return true;
1050 });
1051}
1052
1053void FactsGenerator::handleLifetimeCaptureBy(const FunctionDecl *FD,
1054 ArrayRef<const Expr *> Args) {
1055 if (Args.empty())
1056 return;
1057 // FIXME: Add support for capture_by on constructors.
1059 return;
1060 const auto *Method = dyn_cast<CXXMethodDecl>(FD);
1061 bool HasImplicitObjectArg = Method &&
1062 Method->isImplicitObjectMemberFunction() &&
1064 auto getArgCaptureByAttr =
1065 [FD, HasImplicitObjectArg](
1066 unsigned I) -> std::pair<const LifetimeCaptureByAttr *, QualType> {
1067 if (HasImplicitObjectArg) {
1068 if (I == 0) {
1069 auto *MethodDecl = cast<CXXMethodDecl>(FD);
1070 QualType ParamType = MethodDecl->getFunctionObjectParameterType();
1071 return {getCaptureByAttrFromFunctionType(FD), ParamType};
1072 }
1073 if (I > 0 && I - 1 < FD->getNumParams()) {
1074 const ParmVarDecl *PVD = FD->getParamDecl(I - 1);
1075 return {PVD ? PVD->getAttr<LifetimeCaptureByAttr>() : nullptr,
1076 PVD->getType()};
1077 }
1078 } else {
1079 if (I < FD->getNumParams()) {
1080 const ParmVarDecl *PVD = FD->getParamDecl(I);
1081 return {PVD ? PVD->getAttr<LifetimeCaptureByAttr>() : nullptr,
1082 PVD->getType()};
1083 }
1084 }
1085 return {nullptr, QualType()};
1086 };
1087 for (unsigned I = 0; I < Args.size(); ++I) {
1088 auto [Attr, ParamType] = getArgCaptureByAttr(I);
1089 if (!Attr)
1090 continue;
1091 OriginList *CapturedOriginList = getOriginsList(*Args[I]);
1092 if (!CapturedOriginList)
1093 continue;
1094 // For references to pointer-like types, peel the outer origin (the pointer
1095 // object itself) so that we capture the underlying data (the inner origin).
1096 if (!ParamType.isNull() &&
1097 (ParamType->isReferenceType() &&
1098 isPointerLikeType(ParamType->getPointeeType())) &&
1099 CapturedOriginList->getLength() > 1)
1100 CapturedOriginList = CapturedOriginList->peelOuterOrigin();
1101 for (int CapturingArgIdx : Attr->params()) {
1102 // FIXME: Add support for capturing to Global/unknown.
1103 if (CapturingArgIdx == LifetimeCaptureByAttr::Global ||
1104 CapturingArgIdx == LifetimeCaptureByAttr::Unknown ||
1105 CapturingArgIdx == LifetimeCaptureByAttr::Invalid)
1106 continue;
1107 // FIXME: Diagnose bad CapturingArgIdx.
1108 if (CapturingArgIdx != LifetimeCaptureByAttr::This &&
1109 (CapturingArgIdx < 0 ||
1110 static_cast<size_t>(CapturingArgIdx) >= Args.size()))
1111 continue;
1112 const Expr *CapturedByArg = Args[CapturingArgIdx];
1113 assert(CapturedByArg && "Capturer expression must be valid");
1114
1115 OriginList *Dest = readValue(CapturedByArg);
1116 if (!Dest)
1117 continue;
1118 // KillDest=false because we cannot know if previous captures are being
1119 // replaced or accumulated. Multiple successive captures into the same
1120 // destination must all be tracked, so captured lifetimes are always
1121 // merged.
1122 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
1123 Dest->getOuterOriginID(), CapturedOriginList->getOuterOriginID(),
1124 /*KillDest=*/false));
1125 }
1126 }
1127}
1128
1129void FactsGenerator::handleFunctionCall(const Expr *Call,
1130 bool IsGslConstruction) {
1131 FunctionCallInfo CallInfo(Call);
1132 llvm::ArrayRef<const Expr *> Args = CallInfo.Args;
1133 // The callee may dereference any argument to any depth.
1134 for (const Expr *Arg : Args)
1135 handleUse(Arg);
1136 if (!CallInfo.FD)
1137 return;
1138 OriginList *CallList = getOriginsList(*Call);
1139 // Ignore functions returning values with no origin.
1140 const FunctionDecl *FD = getDeclWithMergedLifetimeBoundAttrs(CallInfo.FD);
1141 if (!FD)
1142 return;
1143 handleInvalidatingCall(Call, FD, Args);
1144 handleDestructiveCall(Call, FD, Args);
1145 handleMovedArgsInCall(FD, Args);
1146 handleImplicitObjectFieldUses(Call, FD);
1147 handleLifetimeCaptureBy(FD, Args);
1148 if (!CallList)
1149 return;
1150 if (isStdReferenceCast(FD)) {
1151 assert(Args.size() == 1 &&
1152 "std reference cast builtins take exactly one argument");
1153 // std reference-cast functions like std::move return a result that refers
1154 // to the same object as the argument, so propagate the full origins.
1155 flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
1156 return;
1157 }
1158 auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool {
1159 const auto *Method = dyn_cast<CXXMethodDecl>(FD);
1160 if (!Method || !Method->isInstance())
1161 return false;
1162 return I == 0 &&
1163 isGslPointerType(Method->getFunctionObjectParameterType()) &&
1165 /*RunningUnderLifetimeSafety=*/true);
1166 };
1167 if (Args.empty())
1168 return;
1169 bool KillSrc = true;
1170 for (unsigned I = 0; I < Args.size(); ++I) {
1171 OriginList *ArgList = getOriginsList(*Args[I]);
1172 if (!ArgList)
1173 continue;
1174 bool ShouldTrackArg = getTrackedArgInfo(FD, Args, I).has_value();
1175 if (IsGslConstruction) {
1176 // TODO: document with code example.
1177 // std::string_view(const std::string_view& from)
1178 if (isGslPointerType(Args[I]->getType())) {
1179 assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
1180 ArgList = readValue(Args[I]);
1181 }
1182 if (isGslOwnerType(Args[I]->getType())) {
1183 // The constructed gsl::Pointer borrows from the Owner's storage, not
1184 // from what the Owner itself borrows, so only the outermost origin is
1185 // needed.
1186 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
1187 CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
1188 KillSrc));
1189 KillSrc = false;
1190 } else if (ShouldTrackArg) {
1191 // Only flow the outer origin here. For lifetimebound args in
1192 // gsl::Pointer construction, we do not have enough information to
1193 // safely match inner origins, so the source and
1194 // destination origin lists may have different lengths.
1195 // FIXME: Handle origin-shape mismatches gracefully so we can also flow
1196 // inner origins.
1197 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
1198 CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
1199 KillSrc));
1200 KillSrc = false;
1201 }
1202 } else if (shouldTrackPointerImplicitObjectArg(I)) {
1203 assert(ArgList->getLength() >= 2 &&
1204 "Object arg of pointer type should have at least two origins");
1205 // See through the GSLPointer reference to see the pointer's value.
1206 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
1207 CallList->getOuterOriginID(),
1208 ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
1209 KillSrc = false;
1210 } else if (ShouldTrackArg) {
1211 // Lifetimebound on a non-GSL-ctor function means the returned
1212 // pointer/reference itself must not outlive the arguments. This
1213 // only constrains the top-level origin.
1214 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
1215 CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
1216 KillSrc = false;
1217 }
1218 }
1219}
1220
1221/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
1222/// If so, creates a `TestPointFact` and returns true.
1223bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
1224 if (!FCE->getType()->isVoidType())
1225 return false;
1226
1227 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
1228 if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
1229 llvm::StringRef LiteralValue = SL->getString();
1230 const std::string Prefix = "__lifetime_test_point_";
1231
1232 if (LiteralValue.starts_with(Prefix)) {
1233 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
1234 CurrentBlockFacts.push_back(
1235 FactMgr.createFact<TestPointFact>(Annotation));
1236 return true;
1237 }
1238 }
1239 return false;
1240}
1241
1242bool FactsGenerator::namesDeclStorage(const OriginList *List) const {
1243 return FactMgr.getOriginMgr()
1244 .getOrigin(List->getOuterOriginID())
1245 .NamesDeclStorage;
1246}
1247
1248void FactsGenerator::handleAccess(const Expr *E) {
1249 OriginList *List = getOriginsList(*E);
1250 if (!List || namesDeclStorage(List))
1251 return;
1252 CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(
1253 *E,
1254 FactMgr.getOriginMgr().createSingleOriginList(List->getOuterOriginID())));
1255}
1256
1257void FactsGenerator::handleUse(const Expr *E) {
1258 OriginList *List = getOriginsList(*E);
1259 if (List && namesDeclStorage(List))
1260 List = List->peelOuterOrigin();
1261 if (List)
1262 CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(*E, List));
1263}
1264
1265// Issues a placeholder loan (this->field) for each origin-carrying field of the
1266// class and its bases. It represents the storage the field refers to, as
1267// opposed to the field itself (covered by $this).
1268void FactsGenerator::issueFieldPlaceholderLoans(
1269 const CXXRecordDecl *ClassDef, llvm::SmallVectorImpl<Fact *> &Facts) {
1270 assert(ClassDef && "class methods have a defined parent");
1271 // Eagerly instantiated methods may see their class still being defined.
1272 assert(ClassDef->isCompleteDefinition() || ClassDef->isBeingDefined());
1273 auto IssueForRecord = [&](const CXXRecordDecl *RD) {
1274 for (const FieldDecl *Field : RD->fields()) {
1275 // Skip private fields of bases as they cannot be named in ClassDef.
1276 if (RD != ClassDef && Field->getAccess() == AS_private)
1277 continue;
1278
1279 if (Field->isAnonymousStructOrUnion()) {
1280 if (const CXXRecordDecl *Sub = Field->getType()->getAsCXXRecordDecl()) {
1281 if (const CXXRecordDecl *SubDef = Sub->getDefinition()) {
1282 // Anonymous records cannot have base classes and they can only have
1283 // public fields. Sema rejects them with an error otherwise.
1284 issueFieldPlaceholderLoans(SubDef, Facts);
1285 continue;
1286 }
1287 }
1288 }
1289 OriginList *FieldList = getOriginsList(*Field);
1290 if (!FieldList)
1291 continue;
1292 const Loan *L = FactMgr.getLoanMgr().createPlaceholderLoan(Field);
1293 Facts.push_back(FactMgr.createFact<IssueFact>(
1294 L->getID(), FieldList->getOuterOriginID()));
1295 }
1296 return true;
1297 };
1298 IssueForRecord(ClassDef);
1299 ClassDef->forallBases(IssueForRecord);
1300}
1301
1302// Creates an IssueFact for a new placeholder loan for each pointer or reference
1303// parameter, the implicit 'this', and (if enabled) each origin-carrying field
1304// at the function's entry.
1305llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
1306 const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl());
1307 if (!FD)
1308 return {};
1309
1310 llvm::SmallVector<Fact *> PlaceholderLoanFacts;
1311 if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) {
1312 OriginList *List = *ThisOrigins;
1313 const auto *MD = cast<CXXMethodDecl>(FD);
1314 const Loan *L = FactMgr.getLoanMgr().createPlaceholderLoan(MD);
1315 PlaceholderLoanFacts.push_back(
1316 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
1317 if (LSOpts.CheckFieldOriginEscape && !isa<CXXConstructorDecl>(MD))
1318 issueFieldPlaceholderLoans(MD->getParent()->getDefinition(),
1319 PlaceholderLoanFacts);
1320 }
1321 for (const ParmVarDecl *PVD : FD->parameters()) {
1322 OriginList *List = getOriginsList(*PVD);
1323 if (!List)
1324 continue;
1325 const Loan *L = FactMgr.getLoanMgr().createPlaceholderLoan(PVD);
1326 PlaceholderLoanFacts.push_back(
1327 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
1328 }
1329 return PlaceholderLoanFacts;
1330}
1331
1332} // 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.
QualType getBaseElementType(const ArrayType *VAT) const
Return the innermost element type of an array type.
AbstractConditionalOperator - An abstract base class for ConditionalOperator and BinaryConditionalOpe...
Definition Expr.h:4422
Expr * getTrueExpr() const
getTrueExpr - Return the subexpression representing the value of the expression if the condition eval...
Definition Expr.h:4606
Expr * getFalseExpr() const
getFalseExpr - Return the subexpression representing the value of the expression if the condition eva...
Definition Expr.h:4612
ASTContext & getASTContext() const
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition Expr.h:2790
inputs_range inputs()
Definition Stmt.h:3404
bool isOutputPlusConstraint(unsigned i) const
isOutputPlusConstraint - Return true if the specified output constraint is a "+" constraint (which is...
Definition Stmt.h:3359
unsigned getNumOutputs() const
Definition Stmt.h:3349
A builtin binary operation expression such as "x + y" or "x <= y".
Definition Expr.h:4107
Expr * getLHS() const
Definition Expr.h:4157
Expr * getRHS() const
Definition Expr.h:4159
static bool isAdditiveOp(Opcode Opc)
Definition Expr.h:4193
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4243
static bool isCompoundAssignmentOp(Opcode Opc)
Definition Expr.h:4248
Opcode getOpcode() const
Definition Expr.h:4152
Represents a single basic block in a source-level CFG.
Definition CFG.h:651
pred_range preds()
Definition CFG.h:1037
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:113
Represents C++ base or member initializer from constructor's initialization list.
Definition CFG.h:232
Represents the point where the lifetime of an automatic object ends.
Definition CFG.h:321
const VarDecl * getVarDecl() const
Definition CFG.h:326
LLVM_ATTRIBUTE_RETURNS_NONNULL const Stmt * getTriggerStmt() const
Definition CFG.h:300
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1294
CFGBlock & getExit()
Definition CFG.h:1410
CFGBlock & getEntry()
Definition CFG.h:1408
Represents binding an expression to a temporary.
Definition ExprCXX.h:1498
const Expr * getSubExpr() const
Definition ExprCXX.h:1520
Represents a call to a C++ constructor.
Definition ExprCXX.h:1553
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1696
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1616
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1693
bool isCopyOrMoveConstructor(unsigned &TypeQuals) const
Determine whether this is a copy or move constructor.
Definition DeclCXX.cpp:3069
Represents a C++ base or member initializer.
Definition DeclCXX.h:2407
Expr * getInit() const
Get the initializer.
Definition DeclCXX.h:2609
FieldDecl * getAnyMember() const
Definition DeclCXX.h:2553
A use of a default initializer in a constructor or in aggregate initialization.
Definition ExprCXX.h:1382
Expr * getExpr()
Get the initialization expression that will be used.
Definition ExprCXX.cpp:1137
Represents a delete expression for memory deallocation and destructor calls, e.g.
Definition ExprCXX.h:2631
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Definition ExprCXX.h:1836
Represents a call to a member function that may be written either with member call syntax (e....
Definition ExprCXX.h:184
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition ExprCXX.cpp:754
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)".
Definition ExprCXX.h:2360
The null pointer literal (C++11 [lex.nullptr])
Definition ExprCXX.h:773
A call to an overloaded operator written using operator syntax.
Definition ExprCXX.h:86
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Definition ExprCXX.h:116
A C++ throw-expression (C++ [except.throw]).
Definition ExprCXX.h:1213
const Expr * getSubExpr() const
Definition ExprCXX.h:1233
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition ExprCXX.h:853
Expr * getExprOperand() const
Definition ExprCXX.h:900
bool isPotentiallyEvaluated() const
Determine whether this typeid has a type operand which is potentially evaluated, per C++11 [expr....
Definition ExprCXX.cpp:136
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:3012
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3216
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3195
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3203
Decl * getCalleeDecl()
Definition Expr.h:3189
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition Expr.h:3745
CastKind getCastKind() const
Definition Expr.h:3789
Expr * getSubExpr()
Definition Expr.h:3795
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1315
NamedDecl * getFoundDecl()
Get the NamedDecl through which this reference occurred.
Definition Expr.h:1426
ValueDecl * getDecl()
Definition Expr.h:1383
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition Stmt.h:1642
decl_range decls()
Definition Stmt.h:1690
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:1137
bool hasAttr() const
Definition DeclBase.h:586
This represents one expression.
Definition Expr.h:114
bool isGLValue() const
Definition Expr.h:289
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3129
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3125
QualType getType() const
Definition Expr.h:146
Represents a member of a struct/union/class.
Definition Decl.h:3311
Represents a function declaration or definition.
Definition Decl.h:2071
bool isDefaulted() const
Whether this function is defaulted.
Definition Decl.h:2524
This represents a GCC inline-assembly statement extension.
Definition Stmt.h:3459
Expr * getOutputExpr(unsigned i)
Definition Stmt.cpp:582
Describes an C or C++ initializer list.
Definition Expr.h:5377
unsigned getNumInits() const
Definition Expr.h:5410
const Expr * getInit(unsigned Init) const
Definition Expr.h:5443
Describes the capture of a variable or of this, or of a C++1y init-capture.
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Definition ExprCXX.h:1973
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Definition ExprCXX.h:4974
StorageDuration getStorageDuration() const
Retrieve the storage duration for the materialized temporary.
Definition ExprCXX.h:4999
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
Definition ExprCXX.h:4991
ValueDecl * getExtendingDecl()
Get the declaration which triggered the lifetime-extension of this temporary, if any.
Definition ExprCXX.h:5024
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition Expr.h:3433
ValueDecl * getMemberDecl() const
Retrieve the member declaration to which this expression refers.
Definition Expr.h:3516
Expr * getBase() const
Definition Expr.h:3510
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3403
A (possibly-)qualified type.
Definition TypeBase.h:938
DestructionKind isDestructedType() const
Returns a nonzero value if objects of this type require non-trivial work to clean up after.
Definition TypeBase.h:1561
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition Stmt.h:3173
Expr * getRetValue()
Definition Stmt.h:3200
Encodes a location in the source.
StmtExpr - This is the GNU Statement Expression extension: ({int X=4; X;}).
Definition Expr.h:4664
CompoundStmt * getSubStmt()
Definition Expr.h:4681
RetTy Visit(PTR(Stmt) S, ParamTys... P)
Definition StmtVisitor.h:45
SourceLocation getEndLoc() const LLVM_READONLY
Definition Stmt.cpp:367
bool isVoidType() const
Definition TypeBase.h:9200
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isPointerType() const
Definition TypeBase.h:8815
bool isPointerOrReferenceType() const
Definition TypeBase.h:8819
bool isFunctionType() const
Definition TypeBase.h:8811
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition Expr.h:2313
Expr * getSubExpr() const
Definition Expr.h:2354
Opcode getOpcode() const
Definition Expr.h:2349
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:714
QualType getType() const
Definition Decl.h:725
Represents a variable declaration or definition.
Definition Decl.h:934
static UseSite scopeExit(SourceLocation Loc, const VarDecl &VD, UseKind Kind)
Definition Facts.h:60
Represents the storage location being borrowed, e.g., a specific stack variable or a field within it:...
Definition Loans.h:127
FactType * createFact(Args &&...args)
Definition Facts.h:451
An abstract base class for a single, atomic lifetime-relevant event.
Definition Facts.h:99
const T * getAs() const
Definition Facts.h:141
void VisitDeclRefExpr(const DeclRefExpr *DRE)
void VisitBinaryOperator(const BinaryOperator *BO)
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE)
void VisitCXXConstructExpr(const CXXConstructExpr *CCE)
void VisitCXXTypeidExpr(const CXXTypeidExpr *TE)
void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO)
Visits conditional operators (e.g., cond ?
void VisitCXXDeleteExpr(const CXXDeleteExpr *DE)
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE)
void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *DIE)
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 VisitCXXThrowExpr(const CXXThrowExpr *TE)
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE)
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE)
Represents that an origin's storage has been invalidated by a container operation (e....
Definition Facts.h:356
Loan * createLoan(AccessPath Path, const Expr *IssueExpr=nullptr)
Definition Loans.h:229
Represents a component of an access path: either a named field access or an abstract unnamed interior...
Definition Loans.h:205
A list of origins representing levels of indirection for pointer-like types.
Definition Origins.h:105
OriginList * peelOuterOrigin() const
Definition Origins.h:109
Represents that an origin escapes via a return statement.
Definition Facts.h:251
bool Sub(InterpState &S, CodePtr OpPC)
Definition Interp.h:426
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
static const Loan * createLoan(FactManager &FactMgr, const ValueDecl &VD, const Expr *IssueExpr)
Creates a loan for the storage path of a given declaration.
static const CFGBlock * findPredBlockForExpr(const CFGBlock *MergeBlock, const Expr *ArmExpr)
bool doesDeclHaveStorage(const ValueDecl *D)
Returns true if the declaration has its own storage that can be borrowed.
Definition Origins.cpp:197
bool isGslPointerType(QualType QT)
bool isStdCallableWrapperType(const CXXRecordDecl *RD)
bool shouldTrackImplicitObjectArg(const Expr &ImplicitObjectArgument, const CXXMethodDecl *Callee, bool RunningUnderLifetimeSafety)
bool isPointerLikeType(QualType QT)
bool isUniquePtrRelease(const CXXMethodDecl &MD)
bool isStdReferenceCast(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInvalidationMethod(const CXXMethodDecl &MD)
bool destructsFirstArg(const FunctionDecl &FD)
bool isGslOwnerType(QualType QT)
std::optional< LifetimeBoundParamInfo > getTrackedArgInfo(const FunctionDecl *FD, llvm::ArrayRef< const Expr * > Args, unsigned I)
const LifetimeCaptureByAttr * getCaptureByAttrFromFunctionType(const FunctionDecl *FD)
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ AS_private
Definition Specifiers.h:130
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ SD_Automatic
Automatic storage duration (most local variables).
Definition Specifiers.h:343
U cast(CodeGen::Address addr)
Definition Address.h:327
bool CheckDestructorUses
Whether to model non-trivial destructors at scope exit as uses.