//===- Loans.h - Loan and Access Path Definitions --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the Loan and AccessPath structures, which represent // borrows of storage locations, and the LoanManager, which manages the // creation and retrieval of loans during lifetime analysis. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H #include "clang/AST/Decl.h" #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" #include "llvm/Support/raw_ostream.h" namespace clang::lifetimes::internal { using LoanID = utils::ID; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) { return OS << ID.Value; } /// Represents the storage location being borrowed, e.g., a specific stack /// variable. /// TODO: Model access paths of other types, e.g., s.field, heap and globals. struct AccessPath { const clang::ValueDecl *D; AccessPath(const clang::ValueDecl *D) : D(D) {} }; /// An abstract base class for a single "Loan" which represents lending a /// storage in memory. class Loan { /// TODO: Represent opaque loans. /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it /// is represented as empty LoanSet public: enum class Kind : uint8_t { /// A loan with an access path to a storage location. Path, /// A non-expiring placeholder loan for a parameter, representing a borrow /// from the function's caller. Placeholder }; Loan(Kind K, LoanID ID) : K(K), ID(ID) {} virtual ~Loan() = default; Kind getKind() const { return K; } LoanID getID() const { return ID; } virtual void dump(llvm::raw_ostream &OS) const = 0; private: const Kind K; const LoanID ID; }; /// PathLoan represents lending a storage location that is visible within the /// function's scope (e.g., a local variable on stack). class PathLoan : public Loan { AccessPath Path; /// The expression that creates the loan, e.g., &x. const Expr *IssueExpr; public: PathLoan(LoanID ID, AccessPath Path, const Expr *IssueExpr) : Loan(Kind::Path, ID), Path(Path), IssueExpr(IssueExpr) {} const AccessPath &getAccessPath() const { return Path; } const Expr *getIssueExpr() const { return IssueExpr; } void dump(llvm::raw_ostream &OS) const override; static bool classof(const Loan *L) { return L->getKind() == Kind::Path; } }; /// A placeholder loan held by a function parameter, representing a borrow from /// the caller's scope. /// /// Created at function entry for each pointer or reference parameter with an /// origin. Unlike PathLoan, placeholder loans: /// - Have no IssueExpr (created at function entry, not at a borrow site) /// - Have no AccessPath (the borrowed object is not visible to the function) /// - Do not currently expire, but may in the future when modeling function /// invalidations (e.g., vector::push_back) /// /// When a placeholder loan escapes the function (e.g., via return), it /// indicates the parameter should be marked [[clang::lifetimebound]], enabling /// lifetime annotation suggestions. class PlaceholderLoan : public Loan { /// The function parameter that holds this placeholder loan. const ParmVarDecl *PVD; public: PlaceholderLoan(LoanID ID, const ParmVarDecl *PVD) : Loan(Kind::Placeholder, ID), PVD(PVD) {} const ParmVarDecl *getParmVarDecl() const { return PVD; } void dump(llvm::raw_ostream &OS) const override; static bool classof(const Loan *L) { return L->getKind() == Kind::Placeholder; } }; /// Manages the creation, storage and retrieval of loans. class LoanManager { public: LoanManager() = default; template LoanType *createLoan(Args &&...args) { static_assert( std::is_same_v || std::is_same_v, "createLoan can only be used with PathLoan or PlaceholderLoan"); void *Mem = LoanAllocator.Allocate(); auto *NewLoan = new (Mem) LoanType(getNextLoanID(), std::forward(args)...); AllLoans.push_back(NewLoan); return NewLoan; } const Loan *getLoan(LoanID ID) const { assert(ID.Value < AllLoans.size()); return AllLoans[ID.Value]; } llvm::ArrayRef getLoans() const { return AllLoans; } private: LoanID getNextLoanID() { return NextLoanID++; } LoanID NextLoanID{0}; /// TODO(opt): Profile and evaluate the usefullness of small buffer /// optimisation. llvm::SmallVector AllLoans; llvm::BumpPtrAllocator LoanAllocator; }; } // namespace clang::lifetimes::internal #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H