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 for (unsigned I = 0; I < Block->size(); ++I) {
62 const CFGElement &Element = Block->Elements[I];
63 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
64 Visit(CS->getStmt());
65 else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
66 Element.getAs<CFGAutomaticObjDtor>())
67 handleDestructor(*DtorOpt);
68 }
69 FactMgr.addBlockFacts(Block, CurrentBlockFacts);
70 }
71}
72
74 for (const Decl *D : DS->decls())
75 if (const auto *VD = dyn_cast<VarDecl>(D))
76 if (hasOrigin(VD))
77 if (const Expr *InitExpr = VD->getInit())
78 killAndFlowOrigin(*VD, *InitExpr);
79}
80
82 handleUse(DRE);
83 // For non-pointer/non-view types, a reference to the variable's storage
84 // is a borrow. We create a loan for it.
85 // For pointer/view types, we stick to the existing model for now and do
86 // not create an extra origin for the l-value expression itself.
87
88 // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
89 // not sufficient to model the different levels of indirection. The current
90 // single-origin model cannot distinguish between a loan to the variable's
91 // storage and a loan to what it points to. A multi-origin model would be
92 // required for this.
93 if (!isPointerType(DRE->getType())) {
94 if (const Loan *L = createLoan(FactMgr, DRE)) {
95 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
96 CurrentBlockFacts.push_back(
97 FactMgr.createFact<IssueFact>(L->ID, ExprOID));
98 }
99 }
100}
101
103 if (isGslPointerType(CCE->getType())) {
104 handleGSLPointerConstruction(CCE);
105 return;
106 }
107}
108
110 // Specifically for conversion operators,
111 // like `std::string_view p = std::string{};`
112 if (isGslPointerType(MCE->getType()) &&
113 isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl())) {
114 // The argument is the implicit object itself.
115 handleFunctionCall(MCE, MCE->getMethodDecl(),
116 {MCE->getImplicitObjectArgument()},
117 /*IsGslConstruction=*/true);
118 }
119 if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
120 // Construct the argument list, with the implicit 'this' object as the
121 // first argument.
123 Args.push_back(MCE->getImplicitObjectArgument());
124 Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
125
126 handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
127 }
128}
129
131 handleFunctionCall(CE, CE->getDirectCallee(),
132 {CE->getArgs(), CE->getNumArgs()});
133}
134
136 const CXXNullPtrLiteralExpr *N) {
137 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
138 /// pointers can use the same type of loan.
139 FactMgr.getOriginMgr().getOrCreate(*N);
140}
141
143 if (!hasOrigin(ICE))
144 return;
145 // An ImplicitCastExpr node itself gets an origin, which flows from the
146 // origin of its sub-expression (after stripping its own parens/casts).
147 killAndFlowOrigin(*ICE, *ICE->getSubExpr());
148}
149
151 if (UO->getOpcode() == UO_AddrOf) {
152 const Expr *SubExpr = UO->getSubExpr();
153 // Taking address of a pointer-type expression is not yet supported and
154 // will be supported in multi-origin model.
155 if (isPointerType(SubExpr->getType()))
156 return;
157 // The origin of an address-of expression (e.g., &x) is the origin of
158 // its sub-expression (x). This fact will cause the dataflow analysis
159 // to propagate any loans held by the sub-expression's origin to the
160 // origin of this UnaryOperator expression.
161 killAndFlowOrigin(*UO, *SubExpr);
162 }
163}
164
166 if (const Expr *RetExpr = RS->getRetValue()) {
167 if (hasOrigin(RetExpr)) {
168 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
169 CurrentBlockFacts.push_back(FactMgr.createFact<ReturnOfOriginFact>(OID));
170 }
171 }
172}
173
175 if (BO->isAssignmentOp())
176 handleAssignment(BO->getLHS(), BO->getRHS());
177}
178
180 // Assignment operators have special "kill-then-propagate" semantics
181 // and are handled separately.
182 if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
183 handleAssignment(OCE->getArg(0), OCE->getArg(1));
184 return;
185 }
186 handleFunctionCall(OCE, OCE->getDirectCallee(),
187 {OCE->getArgs(), OCE->getNumArgs()},
188 /*IsGslConstruction=*/false);
189}
190
192 const CXXFunctionalCastExpr *FCE) {
193 // Check if this is a test point marker. If so, we are done with this
194 // expression.
195 if (handleTestPoint(FCE))
196 return;
197 if (isGslPointerType(FCE->getType()))
198 killAndFlowOrigin(*FCE, *FCE->getSubExpr());
199}
200
202 if (!hasOrigin(ILE))
203 return;
204 // For list initialization with a single element, like `View{...}`, the
205 // origin of the list itself is the origin of its single element.
206 if (ILE->getNumInits() == 1)
207 killAndFlowOrigin(*ILE, *ILE->getInit(0));
208}
209
211 const MaterializeTemporaryExpr *MTE) {
212 if (!hasOrigin(MTE))
213 return;
214 // A temporary object's origin is the same as the origin of the
215 // expression that initializes it.
216 killAndFlowOrigin(*MTE, *MTE->getSubExpr());
217}
218
219void FactsGenerator::handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
220 /// TODO: Also handle trivial destructors (e.g., for `int`
221 /// variables) which will never have a CFGAutomaticObjDtor node.
222 /// TODO: Handle loans to temporaries.
223 /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
224 /// lifetime ends.
225 const VarDecl *DestructedVD = DtorOpt.getVarDecl();
226 if (!DestructedVD)
227 return;
228 // Iterate through all loans to see if any expire.
229 /// TODO(opt): Do better than a linear search to find loans associated with
230 /// 'DestructedVD'.
231 for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
232 const AccessPath &LoanPath = L.Path;
233 // Check if the loan is for a stack variable and if that variable
234 // is the one being destructed.
235 if (LoanPath.D == DestructedVD)
236 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
237 L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
238 }
239}
240
241void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
242 assert(isGslPointerType(CCE->getType()));
243 if (CCE->getNumArgs() != 1)
244 return;
245 if (hasOrigin(CCE->getArg(0)))
246 killAndFlowOrigin(*CCE, *CCE->getArg(0));
247 else
248 // This could be a new borrow.
249 handleFunctionCall(CCE, CCE->getConstructor(),
250 {CCE->getArgs(), CCE->getNumArgs()},
251 /*IsGslConstruction=*/true);
252}
253
254/// Checks if a call-like expression creates a borrow by passing a value to a
255/// reference parameter, creating an IssueFact if it does.
256/// \param IsGslConstruction True if this is a GSL construction where all
257/// argument origins should flow to the returned origin.
258void FactsGenerator::handleFunctionCall(const Expr *Call,
259 const FunctionDecl *FD,
261 bool IsGslConstruction) {
262 // Ignore functions returning values with no origin.
263 if (!FD || !hasOrigin(Call))
264 return;
265 auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
266 const ParmVarDecl *PVD = nullptr;
267 if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
268 Method && Method->isInstance()) {
269 if (I == 0)
270 // For the 'this' argument, the attribute is on the method itself.
272 if ((I - 1) < Method->getNumParams())
273 // For explicit arguments, find the corresponding parameter
274 // declaration.
275 PVD = Method->getParamDecl(I - 1);
276 } else if (I < FD->getNumParams())
277 // For free functions or static methods.
278 PVD = FD->getParamDecl(I);
279 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
280 };
281 if (Args.empty())
282 return;
283 bool killedSrc = false;
284 for (unsigned I = 0; I < Args.size(); ++I)
285 if (IsGslConstruction || IsArgLifetimeBound(I)) {
286 if (!killedSrc) {
287 killedSrc = true;
288 killAndFlowOrigin(*Call, *Args[I]);
289 } else
290 flowOrigin(*Call, *Args[I]);
291 }
292}
293
294/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
295/// If so, creates a `TestPointFact` and returns true.
296bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
297 if (!FCE->getType()->isVoidType())
298 return false;
299
300 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
301 if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
302 llvm::StringRef LiteralValue = SL->getString();
303 const std::string Prefix = "__lifetime_test_point_";
304
305 if (LiteralValue.starts_with(Prefix)) {
306 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
307 CurrentBlockFacts.push_back(
308 FactMgr.createFact<TestPointFact>(Annotation));
309 return true;
310 }
311 }
312 return false;
313}
314
315void FactsGenerator::handleAssignment(const Expr *LHSExpr,
316 const Expr *RHSExpr) {
317 if (!hasOrigin(LHSExpr))
318 return;
319 // Find the underlying variable declaration for the left-hand side.
320 if (const auto *DRE_LHS =
321 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
322 markUseAsWrite(DRE_LHS);
323 if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
324 // Kill the old loans of the destination origin and flow the new loans
325 // from the source origin.
326 killAndFlowOrigin(*VD_LHS, *RHSExpr);
327 }
328 }
329}
330
331// A DeclRefExpr will be treated as a use of the referenced decl. It will be
332// checked for use-after-free unless it is later marked as being written to
333// (e.g. on the left-hand side of an assignment).
334void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
335 if (isPointerType(DRE->getType())) {
336 UseFact *UF = FactMgr.createFact<UseFact>(DRE);
337 CurrentBlockFacts.push_back(UF);
338 assert(!UseFacts.contains(DRE));
339 UseFacts[DRE] = UF;
340 }
341}
342
343void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
344 if (!isPointerType(DRE->getType()))
345 return;
346 assert(UseFacts.contains(DRE));
347 UseFacts[DRE]->markAsWritten();
348}
349
350} // 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 C++ object destructor implicitly generated for automatic object or temporary bound to cons...
Definition CFG.h:418
const VarDecl * getVarDecl() const
Definition CFG.h:423
const Stmt * getTriggerStmt() const
Definition CFG.h:428
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 a call to a C++ constructor.
Definition ExprCXX.h:1549
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1692
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1612
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1689
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Definition ExprCXX.h:1833
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
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:1611
decl_range decls()
Definition Stmt.h:1659
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:4922
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
Definition ExprCXX.h:4939
A (possibly-)qualified type.
Definition TypeBase.h:937
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition Stmt.h:3160
Expr * getRetValue()
Definition Stmt.h:3187
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:200
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 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