clang 22.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
12#include "llvm/Support/Casting.h"
13#include "llvm/Support/TimeProfiler.h"
14
16using llvm::isa_and_present;
17
18static bool isPointerType(QualType QT) {
20}
21// Check if a type has an origin.
22static bool hasOrigin(const Expr *E) {
23 return E->isGLValue() || isPointerType(E->getType());
24}
25
26static bool hasOrigin(const VarDecl *VD) {
27 return isPointerType(VD->getType());
28}
29
30/// Creates a loan for the storage path of a given declaration reference.
31/// This function should be called whenever a DeclRefExpr represents a borrow.
32/// \param DRE The declaration reference expression that initiates the borrow.
33/// \return The new Loan on success, nullptr otherwise.
34static const PathLoan *createLoan(FactManager &FactMgr,
35 const DeclRefExpr *DRE) {
36 if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
37 AccessPath Path(VD);
38 // The loan is created at the location of the DeclRefExpr.
39 return FactMgr.getLoanMgr().createLoan<PathLoan>(Path, DRE);
40 }
41 return nullptr;
42}
43
45 llvm::TimeTraceScope TimeProfile("FactGenerator");
46 const CFG &Cfg = *AC.getCFG();
47 llvm::SmallVector<Fact *> PlaceholderLoanFacts = issuePlaceholderLoans();
48 // Iterate through the CFG blocks in reverse post-order to ensure that
49 // initializations and destructions are processed in the correct sequence.
50 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
51 CurrentBlockFacts.clear();
52 EscapesInCurrentBlock.clear();
53 if (Block == &Cfg.getEntry())
54 CurrentBlockFacts.append(PlaceholderLoanFacts.begin(),
55 PlaceholderLoanFacts.end());
56 for (unsigned I = 0; I < Block->size(); ++I) {
57 const CFGElement &Element = Block->Elements[I];
58 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
59 Visit(CS->getStmt());
60 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
61 Element.getAs<CFGLifetimeEnds>())
62 handleLifetimeEnds(*LifetimeEnds);
63 }
64 CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
65 EscapesInCurrentBlock.end());
66 FactMgr.addBlockFacts(Block, CurrentBlockFacts);
67 }
68}
69
71 for (const Decl *D : DS->decls())
72 if (const auto *VD = dyn_cast<VarDecl>(D))
73 if (hasOrigin(VD))
74 if (const Expr *InitExpr = VD->getInit())
75 killAndFlowOrigin(*VD, *InitExpr);
76}
77
79 handleUse(DRE);
80 // For non-pointer/non-view types, a reference to the variable's storage
81 // is a borrow. We create a loan for it.
82 // For pointer/view types, we stick to the existing model for now and do
83 // not create an extra origin for the l-value expression itself.
84
85 // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
86 // not sufficient to model the different levels of indirection. The current
87 // single-origin model cannot distinguish between a loan to the variable's
88 // storage and a loan to what it points to. A multi-origin model would be
89 // required for this.
90 if (!isPointerType(DRE->getType())) {
91 if (const Loan *L = createLoan(FactMgr, DRE)) {
92 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
93 CurrentBlockFacts.push_back(
94 FactMgr.createFact<IssueFact>(L->getID(), ExprOID));
95 }
96 }
97}
98
100 if (isGslPointerType(CCE->getType())) {
101 handleGSLPointerConstruction(CCE);
102 return;
103 }
104}
105
107 // Specifically for conversion operators,
108 // like `std::string_view p = std::string{};`
109 if (isGslPointerType(MCE->getType()) &&
110 isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl())) {
111 // The argument is the implicit object itself.
112 handleFunctionCall(MCE, MCE->getMethodDecl(),
113 {MCE->getImplicitObjectArgument()},
114 /*IsGslConstruction=*/true);
115 }
116 if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
117 // Construct the argument list, with the implicit 'this' object as the
118 // first argument.
120 Args.push_back(MCE->getImplicitObjectArgument());
121 Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
122
123 handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
124 }
125}
126
128 handleFunctionCall(CE, CE->getDirectCallee(),
129 {CE->getArgs(), CE->getNumArgs()});
130}
131
133 const CXXNullPtrLiteralExpr *N) {
134 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
135 /// pointers can use the same type of loan.
136 FactMgr.getOriginMgr().getOrCreate(*N);
137}
138
140 if (!hasOrigin(ICE))
141 return;
142 // An ImplicitCastExpr node itself gets an origin, which flows from the
143 // origin of its sub-expression (after stripping its own parens/casts).
144 killAndFlowOrigin(*ICE, *ICE->getSubExpr());
145}
146
148 if (UO->getOpcode() == UO_AddrOf) {
149 const Expr *SubExpr = UO->getSubExpr();
150 // Taking address of a pointer-type expression is not yet supported and
151 // will be supported in multi-origin model.
152 if (isPointerType(SubExpr->getType()))
153 return;
154 // The origin of an address-of expression (e.g., &x) is the origin of
155 // its sub-expression (x). This fact will cause the dataflow analysis
156 // to propagate any loans held by the sub-expression's origin to the
157 // origin of this UnaryOperator expression.
158 killAndFlowOrigin(*UO, *SubExpr);
159 }
160}
161
163 if (const Expr *RetExpr = RS->getRetValue()) {
164 if (hasOrigin(RetExpr)) {
165 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
166 EscapesInCurrentBlock.push_back(
167 FactMgr.createFact<OriginEscapesFact>(OID, RetExpr));
168 }
169 }
170}
171
173 if (BO->isAssignmentOp())
174 handleAssignment(BO->getLHS(), BO->getRHS());
175}
176
178 if (hasOrigin(CO)) {
179 // Merge origins from both branches of the conditional operator.
180 // We kill to clear the initial state and merge both origins into it.
181 killAndFlowOrigin(*CO, *CO->getTrueExpr());
182 flowOrigin(*CO, *CO->getFalseExpr());
183 }
184}
185
187 // Assignment operators have special "kill-then-propagate" semantics
188 // and are handled separately.
189 if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
190 handleAssignment(OCE->getArg(0), OCE->getArg(1));
191 return;
192 }
193 handleFunctionCall(OCE, OCE->getDirectCallee(),
194 {OCE->getArgs(), OCE->getNumArgs()},
195 /*IsGslConstruction=*/false);
196}
197
199 const CXXFunctionalCastExpr *FCE) {
200 // Check if this is a test point marker. If so, we are done with this
201 // expression.
202 if (handleTestPoint(FCE))
203 return;
204 if (isGslPointerType(FCE->getType()))
205 killAndFlowOrigin(*FCE, *FCE->getSubExpr());
206}
207
209 if (!hasOrigin(ILE))
210 return;
211 // For list initialization with a single element, like `View{...}`, the
212 // origin of the list itself is the origin of its single element.
213 if (ILE->getNumInits() == 1)
214 killAndFlowOrigin(*ILE, *ILE->getInit(0));
215}
216
218 const MaterializeTemporaryExpr *MTE) {
219 if (!hasOrigin(MTE))
220 return;
221 // A temporary object's origin is the same as the origin of the
222 // expression that initializes it.
223 killAndFlowOrigin(*MTE, *MTE->getSubExpr());
224}
225
226void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
227 /// TODO: Handle loans to temporaries.
228 const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
229 if (!LifetimeEndsVD)
230 return;
231 // Iterate through all loans to see if any expire.
232 for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
233 if (const auto *BL = dyn_cast<PathLoan>(Loan)) {
234 // Check if the loan is for a stack variable and if that variable
235 // is the one being destructed.
236 if (BL->getAccessPath().D == LifetimeEndsVD)
237 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
238 BL->getID(), LifetimeEnds.getTriggerStmt()->getEndLoc()));
239 }
240 }
241}
242
243void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
244 assert(isGslPointerType(CCE->getType()));
245 if (CCE->getNumArgs() != 1)
246 return;
247 if (hasOrigin(CCE->getArg(0)))
248 killAndFlowOrigin(*CCE, *CCE->getArg(0));
249 else
250 // This could be a new borrow.
251 handleFunctionCall(CCE, CCE->getConstructor(),
252 {CCE->getArgs(), CCE->getNumArgs()},
253 /*IsGslConstruction=*/true);
254}
255
256/// Checks if a call-like expression creates a borrow by passing a value to a
257/// reference parameter, creating an IssueFact if it does.
258/// \param IsGslConstruction True if this is a GSL construction where all
259/// argument origins should flow to the returned origin.
260void FactsGenerator::handleFunctionCall(const Expr *Call,
261 const FunctionDecl *FD,
262 ArrayRef<const Expr *> Args,
263 bool IsGslConstruction) {
264 // Ignore functions returning values with no origin.
265 if (!FD || !hasOrigin(Call))
266 return;
267 auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
268 const ParmVarDecl *PVD = nullptr;
269 if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
270 Method && Method->isInstance()) {
271 if (I == 0)
272 // For the 'this' argument, the attribute is on the method itself.
274 if ((I - 1) < Method->getNumParams())
275 // For explicit arguments, find the corresponding parameter
276 // declaration.
277 PVD = Method->getParamDecl(I - 1);
278 } else if (I < FD->getNumParams())
279 // For free functions or static methods.
280 PVD = FD->getParamDecl(I);
281 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
282 };
283 if (Args.empty())
284 return;
285 bool killedSrc = false;
286 for (unsigned I = 0; I < Args.size(); ++I)
287 if (IsGslConstruction || IsArgLifetimeBound(I)) {
288 if (!killedSrc) {
289 killedSrc = true;
290 killAndFlowOrigin(*Call, *Args[I]);
291 } else
292 flowOrigin(*Call, *Args[I]);
293 }
294}
295
296/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
297/// If so, creates a `TestPointFact` and returns true.
298bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
299 if (!FCE->getType()->isVoidType())
300 return false;
301
302 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
303 if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
304 llvm::StringRef LiteralValue = SL->getString();
305 const std::string Prefix = "__lifetime_test_point_";
306
307 if (LiteralValue.starts_with(Prefix)) {
308 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
309 CurrentBlockFacts.push_back(
310 FactMgr.createFact<TestPointFact>(Annotation));
311 return true;
312 }
313 }
314 return false;
315}
316
317void FactsGenerator::handleAssignment(const Expr *LHSExpr,
318 const Expr *RHSExpr) {
319 if (!hasOrigin(LHSExpr))
320 return;
321 // Find the underlying variable declaration for the left-hand side.
322 if (const auto *DRE_LHS =
323 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
324 markUseAsWrite(DRE_LHS);
325 if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
326 // Kill the old loans of the destination origin and flow the new loans
327 // from the source origin.
328 killAndFlowOrigin(*VD_LHS, *RHSExpr);
329 }
330 }
331}
332
333// A DeclRefExpr will be treated as a use of the referenced decl. It will be
334// checked for use-after-free unless it is later marked as being written to
335// (e.g. on the left-hand side of an assignment).
336void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
337 if (isPointerType(DRE->getType())) {
338 UseFact *UF = FactMgr.createFact<UseFact>(DRE, FactMgr.getOriginMgr());
339 CurrentBlockFacts.push_back(UF);
340 assert(!UseFacts.contains(DRE));
341 UseFacts[DRE] = UF;
342 }
343}
344
345void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
346 if (!isPointerType(DRE->getType()))
347 return;
348 assert(UseFacts.contains(DRE));
349 UseFacts[DRE]->markAsWritten();
350}
351
352// Creates an IssueFact for a new placeholder loan for each pointer or reference
353// parameter at the function's entry.
354llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
355 const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl());
356 if (!FD)
357 return {};
358
359 llvm::SmallVector<Fact *> PlaceholderLoanFacts;
360 for (const ParmVarDecl *PVD : FD->parameters()) {
361 if (hasOrigin(PVD)) {
362 const PlaceholderLoan *L =
363 FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(PVD);
364 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*PVD);
365 PlaceholderLoanFacts.push_back(
366 FactMgr.createFact<IssueFact>(L->getID(), OID));
367 }
368 }
369 return PlaceholderLoanFacts;
370}
371
372} // namespace clang::lifetimes::internal
A builtin binary operation expression such as "x + y" or "x <= y".
Definition Expr.h:3972
Expr * getLHS() const
Definition Expr.h:4022
Expr * getRHS() const
Definition Expr.h:4024
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4108
Represents a single basic block in a source-level CFG.
Definition CFG.h:605
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:109
Represents the point where the lifetime of an automatic object ends.
Definition CFG.h:293
const Stmt * getTriggerStmt() const
Definition CFG.h:302
const VarDecl * getVarDecl() const
Definition CFG.h:298
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1222
CFGBlock & getEntry()
Definition CFG.h:1331
Represents a call to a C++ constructor.
Definition ExprCXX.h:1548
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Definition ExprCXX.h:1831
Represents a call to a member function that may be written either with member call syntax (e....
Definition ExprCXX.h:179
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:2129
The null pointer literal (C++11 [lex.nullptr])
Definition ExprCXX.h:768
A call to an overloaded operator written using operator syntax.
Definition ExprCXX.h:84
static bool isAssignmentOp(OverloadedOperatorKind Opc)
Definition ExprCXX.h:119
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2877
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3060
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3068
Expr ** getArgs()
Retrieve the call arguments.
Definition Expr.h:3071
Decl * getCalleeDecl()
Definition Expr.h:3054
Expr * getSubExpr()
Definition Expr.h:3660
ConditionalOperator - The ?
Definition Expr.h:4325
Expr * getFalseExpr() const
getFalseExpr - Return the subexpression representing the value of the expression if the condition eva...
Definition Expr.h:4357
Expr * getTrueExpr() const
getTrueExpr - Return the subexpression representing the value of the expression if the condition eval...
Definition Expr.h:4352
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1270
ValueDecl * getDecl()
Definition Expr.h:1338
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition Stmt.h:1621
decl_range decls()
Definition Stmt.h:1669
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:112
bool isGLValue() const
Definition Expr.h:287
QualType getType() const
Definition Expr.h:144
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition Expr.h:3787
Describes an C or C++ initializer list.
Definition Expr.h:5233
unsigned getNumInits() const
Definition Expr.h:5263
const Expr * getInit(unsigned Init) const
Definition Expr.h:5287
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Definition ExprCXX.h:4920
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
Definition ExprCXX.h:4937
A (possibly-)qualified type.
Definition TypeBase.h:937
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition Stmt.h:3150
Expr * getRetValue()
Definition Stmt.h:3177
RetTy Visit(PTR(Stmt) S, ParamTys... P)
Definition StmtVisitor.h:45
SourceLocation getEndLoc() const LLVM_READONLY
Definition Stmt.cpp:362
bool isPointerOrReferenceType() const
Definition TypeBase.h:8519
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition Expr.h:2244
Expr * getSubExpr() const
Definition Expr.h:2285
Opcode getOpcode() const
Definition Expr.h:2280
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:926
FactType * createFact(Args &&...args)
Definition Facts.h:212
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 VisitUnaryOperator(const UnaryOperator *UO)
void VisitConditionalOperator(const ConditionalOperator *CO)
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE)
llvm::ArrayRef< const Loan * > getLoans() const
Definition Loans.h:136
LoanType * createLoan(Args &&...args)
Definition Loans.h:120
An abstract base class for a single "Loan" which represents lending a storage in memory.
Definition Loans.h:39
PathLoan represents lending a storage location that is visible within the function's scope (e....
Definition Loans.h:67
static const PathLoan * createLoan(FactManager &FactMgr, const DeclRefExpr *DRE)
Creates a loan for the storage path of a given declaration reference.
static bool hasOrigin(const Expr *E)
utils::ID< struct OriginTag > OriginID
Definition Origins.h:23
static bool isPointerType(QualType QT)
bool isGslPointerType(QualType QT)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
#define false
Definition stdbool.h:26
Represents the storage location being borrowed, e.g., a specific stack variable.
Definition Loans.h:31