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 isGslPointerType(QualType QT) {
19 if (const auto *RD = QT->getAsCXXRecordDecl()) {
20 // We need to check the template definition for specializations.
21 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
22 return CTSD->getSpecializedTemplate()
23 ->getTemplatedDecl()
24 ->hasAttr<PointerAttr>();
25 return RD->hasAttr<PointerAttr>();
26 }
27 return false;
28}
29
30static bool isPointerType(QualType QT) {
32}
33// Check if a type has an origin.
34static bool hasOrigin(const Expr *E) {
35 return E->isGLValue() || isPointerType(E->getType());
36}
37
38static bool hasOrigin(const VarDecl *VD) {
39 return isPointerType(VD->getType());
40}
41
42/// Creates a loan for the storage path of a given declaration reference.
43/// This function should be called whenever a DeclRefExpr represents a borrow.
44/// \param DRE The declaration reference expression that initiates the borrow.
45/// \return The new Loan on success, nullptr otherwise.
46static const Loan *createLoan(FactManager &FactMgr, const DeclRefExpr *DRE) {
47 if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
48 AccessPath Path(VD);
49 // The loan is created at the location of the DeclRefExpr.
50 return &FactMgr.getLoanMgr().addLoan(Path, DRE);
51 }
52 return nullptr;
53}
54
56 llvm::TimeTraceScope TimeProfile("FactGenerator");
57 // Iterate through the CFG blocks in reverse post-order to ensure that
58 // initializations and destructions are processed in the correct sequence.
59 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
60 CurrentBlockFacts.clear();
61 EscapesInCurrentBlock.clear();
62 for (unsigned I = 0; I < Block->size(); ++I) {
63 const CFGElement &Element = Block->Elements[I];
64 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
65 Visit(CS->getStmt());
66 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
67 Element.getAs<CFGLifetimeEnds>())
68 handleLifetimeEnds(*LifetimeEnds);
69 }
70 CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
71 EscapesInCurrentBlock.end());
72 FactMgr.addBlockFacts(Block, CurrentBlockFacts);
73 }
74}
75
77 for (const Decl *D : DS->decls())
78 if (const auto *VD = dyn_cast<VarDecl>(D))
79 if (hasOrigin(VD))
80 if (const Expr *InitExpr = VD->getInit())
81 killAndFlowOrigin(*VD, *InitExpr);
82}
83
85 handleUse(DRE);
86 // For non-pointer/non-view types, a reference to the variable's storage
87 // is a borrow. We create a loan for it.
88 // For pointer/view types, we stick to the existing model for now and do
89 // not create an extra origin for the l-value expression itself.
90
91 // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
92 // not sufficient to model the different levels of indirection. The current
93 // single-origin model cannot distinguish between a loan to the variable's
94 // storage and a loan to what it points to. A multi-origin model would be
95 // required for this.
96 if (!isPointerType(DRE->getType())) {
97 if (const Loan *L = createLoan(FactMgr, DRE)) {
98 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
99 CurrentBlockFacts.push_back(
100 FactMgr.createFact<IssueFact>(L->ID, ExprOID));
101 }
102 }
103}
104
106 if (isGslPointerType(CCE->getType())) {
107 handleGSLPointerConstruction(CCE);
108 return;
109 }
110}
111
113 // Specifically for conversion operators,
114 // like `std::string_view p = std::string{};`
115 if (isGslPointerType(MCE->getType()) &&
116 isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl())) {
117 // The argument is the implicit object itself.
118 handleFunctionCall(MCE, MCE->getMethodDecl(),
119 {MCE->getImplicitObjectArgument()},
120 /*IsGslConstruction=*/true);
121 }
122 if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
123 // Construct the argument list, with the implicit 'this' object as the
124 // first argument.
126 Args.push_back(MCE->getImplicitObjectArgument());
127 Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
128
129 handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
130 }
131}
132
134 handleFunctionCall(CE, CE->getDirectCallee(),
135 {CE->getArgs(), CE->getNumArgs()});
136}
137
139 const CXXNullPtrLiteralExpr *N) {
140 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
141 /// pointers can use the same type of loan.
142 FactMgr.getOriginMgr().getOrCreate(*N);
143}
144
146 if (!hasOrigin(ICE))
147 return;
148 // An ImplicitCastExpr node itself gets an origin, which flows from the
149 // origin of its sub-expression (after stripping its own parens/casts).
150 killAndFlowOrigin(*ICE, *ICE->getSubExpr());
151}
152
154 if (UO->getOpcode() == UO_AddrOf) {
155 const Expr *SubExpr = UO->getSubExpr();
156 // Taking address of a pointer-type expression is not yet supported and
157 // will be supported in multi-origin model.
158 if (isPointerType(SubExpr->getType()))
159 return;
160 // The origin of an address-of expression (e.g., &x) is the origin of
161 // its sub-expression (x). This fact will cause the dataflow analysis
162 // to propagate any loans held by the sub-expression's origin to the
163 // origin of this UnaryOperator expression.
164 killAndFlowOrigin(*UO, *SubExpr);
165 }
166}
167
169 if (const Expr *RetExpr = RS->getRetValue()) {
170 if (hasOrigin(RetExpr)) {
171 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
172 EscapesInCurrentBlock.push_back(
173 FactMgr.createFact<OriginEscapesFact>(OID, RetExpr));
174 }
175 }
176}
177
179 if (BO->isAssignmentOp())
180 handleAssignment(BO->getLHS(), BO->getRHS());
181}
182
184 if (hasOrigin(CO)) {
185 // Merge origins from both branches of the conditional operator.
186 // We kill to clear the initial state and merge both origins into it.
187 killAndFlowOrigin(*CO, *CO->getTrueExpr());
188 flowOrigin(*CO, *CO->getFalseExpr());
189 }
190}
191
193 // Assignment operators have special "kill-then-propagate" semantics
194 // and are handled separately.
195 if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
196 handleAssignment(OCE->getArg(0), OCE->getArg(1));
197 return;
198 }
199 handleFunctionCall(OCE, OCE->getDirectCallee(),
200 {OCE->getArgs(), OCE->getNumArgs()},
201 /*IsGslConstruction=*/false);
202}
203
205 const CXXFunctionalCastExpr *FCE) {
206 // Check if this is a test point marker. If so, we are done with this
207 // expression.
208 if (handleTestPoint(FCE))
209 return;
210 if (isGslPointerType(FCE->getType()))
211 killAndFlowOrigin(*FCE, *FCE->getSubExpr());
212}
213
215 if (!hasOrigin(ILE))
216 return;
217 // For list initialization with a single element, like `View{...}`, the
218 // origin of the list itself is the origin of its single element.
219 if (ILE->getNumInits() == 1)
220 killAndFlowOrigin(*ILE, *ILE->getInit(0));
221}
222
224 const MaterializeTemporaryExpr *MTE) {
225 if (!hasOrigin(MTE))
226 return;
227 // A temporary object's origin is the same as the origin of the
228 // expression that initializes it.
229 killAndFlowOrigin(*MTE, *MTE->getSubExpr());
230}
231
232void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
233 /// TODO: Handle loans to temporaries.
234 const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
235 if (!LifetimeEndsVD)
236 return;
237 // Iterate through all loans to see if any expire.
238 for (const auto &Loan : FactMgr.getLoanMgr().getLoans()) {
239 const AccessPath &LoanPath = Loan.Path;
240 // Check if the loan is for a stack variable and if that variable
241 // is the one being destructed.
242 if (LoanPath.D == LifetimeEndsVD)
243 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
244 Loan.ID, LifetimeEnds.getTriggerStmt()->getEndLoc()));
245 }
246}
247
248void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
249 assert(isGslPointerType(CCE->getType()));
250 if (CCE->getNumArgs() != 1)
251 return;
252 if (hasOrigin(CCE->getArg(0)))
253 killAndFlowOrigin(*CCE, *CCE->getArg(0));
254 else
255 // This could be a new borrow.
256 handleFunctionCall(CCE, CCE->getConstructor(),
257 {CCE->getArgs(), CCE->getNumArgs()},
258 /*IsGslConstruction=*/true);
259}
260
261/// Checks if a call-like expression creates a borrow by passing a value to a
262/// reference parameter, creating an IssueFact if it does.
263/// \param IsGslConstruction True if this is a GSL construction where all
264/// argument origins should flow to the returned origin.
265void FactsGenerator::handleFunctionCall(const Expr *Call,
266 const FunctionDecl *FD,
268 bool IsGslConstruction) {
269 // Ignore functions returning values with no origin.
270 if (!FD || !hasOrigin(Call))
271 return;
272 auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
273 const ParmVarDecl *PVD = nullptr;
274 if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
275 Method && Method->isInstance()) {
276 if (I == 0)
277 // For the 'this' argument, the attribute is on the method itself.
279 if ((I - 1) < Method->getNumParams())
280 // For explicit arguments, find the corresponding parameter
281 // declaration.
282 PVD = Method->getParamDecl(I - 1);
283 } else if (I < FD->getNumParams())
284 // For free functions or static methods.
285 PVD = FD->getParamDecl(I);
286 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
287 };
288 if (Args.empty())
289 return;
290 bool killedSrc = false;
291 for (unsigned I = 0; I < Args.size(); ++I)
292 if (IsGslConstruction || IsArgLifetimeBound(I)) {
293 if (!killedSrc) {
294 killedSrc = true;
295 killAndFlowOrigin(*Call, *Args[I]);
296 } else
297 flowOrigin(*Call, *Args[I]);
298 }
299}
300
301/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
302/// If so, creates a `TestPointFact` and returns true.
303bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
304 if (!FCE->getType()->isVoidType())
305 return false;
306
307 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
308 if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
309 llvm::StringRef LiteralValue = SL->getString();
310 const std::string Prefix = "__lifetime_test_point_";
311
312 if (LiteralValue.starts_with(Prefix)) {
313 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
314 CurrentBlockFacts.push_back(
315 FactMgr.createFact<TestPointFact>(Annotation));
316 return true;
317 }
318 }
319 return false;
320}
321
322void FactsGenerator::handleAssignment(const Expr *LHSExpr,
323 const Expr *RHSExpr) {
324 if (!hasOrigin(LHSExpr))
325 return;
326 // Find the underlying variable declaration for the left-hand side.
327 if (const auto *DRE_LHS =
328 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
329 markUseAsWrite(DRE_LHS);
330 if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
331 // Kill the old loans of the destination origin and flow the new loans
332 // from the source origin.
333 killAndFlowOrigin(*VD_LHS, *RHSExpr);
334 }
335 }
336}
337
338// A DeclRefExpr will be treated as a use of the referenced decl. It will be
339// checked for use-after-free unless it is later marked as being written to
340// (e.g. on the left-hand side of an assignment).
341void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
342 if (isPointerType(DRE->getType())) {
343 UseFact *UF = FactMgr.createFact<UseFact>(DRE, FactMgr.getOriginMgr());
344 CurrentBlockFacts.push_back(UF);
345 assert(!UseFacts.contains(DRE));
346 UseFacts[DRE] = UF;
347 }
348}
349
350void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
351 if (!isPointerType(DRE->getType()))
352 return;
353 assert(UseFacts.contains(DRE));
354 UseFacts[DRE]->markAsWritten();
355}
356
357} // 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 call to a C++ constructor.
Definition ExprCXX.h:1548
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1691
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1611
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1688
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:1610
decl_range decls()
Definition Stmt.h:1658
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:3139
Expr * getRetValue()
Definition Stmt.h:3166
RetTy Visit(PTR(Stmt) S, ParamTys... P)
Definition StmtVisitor.h:45
SourceLocation getEndLoc() const LLVM_READONLY
Definition Stmt.cpp:362
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 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)
Loan & addLoan(AccessPath Path, const Expr *IssueExpr)
Definition Loans.h:59
llvm::ArrayRef< Loan > getLoans() const
Definition Loans.h:68
static bool hasOrigin(const Expr *E)
utils::ID< struct OriginTag > OriginID
Definition Origins.h:23
static bool isPointerType(QualType QT)
static bool isGslPointerType(QualType QT)
static const Loan * createLoan(FactManager &FactMgr, const DeclRefExpr *DRE)
Creates a loan for the storage path of a given declaration reference.
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
const clang::ValueDecl * D
Definition Loans.h:32
Information about a single borrow, or "Loan".
Definition Loans.h:39
LoanID ID
TODO: Represent opaque loans.
Definition Loans.h:43