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