================
@@ -27,140 +31,200 @@ 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.
-class AccessPath {
- // An access path can be:
- // - ValueDecl * , to represent the storage location corresponding to the
- // variable declared in ValueDecl.
- // - MaterializeTemporaryExpr * , to represent the storage location of the
- // temporary object materialized via this MaterializeTemporaryExpr.
- const llvm::PointerUnion<const clang::ValueDecl *,
- const clang::MaterializeTemporaryExpr *>
- P;
-
+/// Represents one step in an access path: either a field access or an
+/// access to an unnamed interior region (denoted by '*').
+///
+/// Examples:
+/// - Field access: `obj.field` has PathElement 'field'
+/// - Interior access: `owner.*` has '*'
+// - In `std::string s; std::string_view v = s;`, v has loan to s.*
+class PathElement {
public:
- AccessPath(const clang::ValueDecl *D) : P(D) {}
- AccessPath(const clang::MaterializeTemporaryExpr *MTE) : P(MTE) {}
+ enum class Kind { Field, Interior };
- const clang::ValueDecl *getAsValueDecl() const {
- return P.dyn_cast<const clang::ValueDecl *>();
+ static PathElement getField(const FieldDecl *FD) {
+ return PathElement(Kind::Field, FD);
}
-
- const clang::MaterializeTemporaryExpr *getAsMaterializeTemporaryExpr() const
{
- return P.dyn_cast<const clang::MaterializeTemporaryExpr *>();
+ static PathElement getInterior() {
+ return PathElement(Kind::Interior, nullptr);
}
- bool operator==(const AccessPath &RHS) const { return P == RHS.P; }
-};
+ bool isField() const { return K == Kind::Field; }
+ bool isInterior() const { return K == Kind::Interior; }
+ const FieldDecl *getFieldDecl() const { return FD; }
-/// 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; }
+ bool operator==(const PathElement &Other) const {
+ return K == Other.K && FD == Other.FD;
+ }
+ bool operator!=(const PathElement &Other) const { return !(*this == Other); }
- virtual void dump(llvm::raw_ostream &OS) const = 0;
+ void dump(llvm::raw_ostream &OS) const {
+ if (isField())
+ OS << "." << FD->getNameAsString();
+ else
+ OS << ".*";
+ }
private:
- const Kind K;
- const LoanID ID;
+ PathElement(Kind K, const FieldDecl *FD) : K(K), FD(FD) {}
+ Kind K;
+ const FieldDecl *FD;
};
-/// 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;
+/// Represents the base of a placeholder access path, which is either a
+/// function parameter or the implicit 'this' object of an instance method.
+/// Placeholder paths never expire within the function scope, as they represent
+/// storage from the caller's scope.
+class PlaceholderBase : public llvm::FoldingSetNode {
+ llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *> ParamOrMethod;
public:
- PathLoan(LoanID ID, AccessPath Path, const Expr *IssueExpr)
- : Loan(Kind::Path, ID), Path(Path), IssueExpr(IssueExpr) {}
+ PlaceholderBase(const ParmVarDecl *PVD) : ParamOrMethod(PVD) {}
+ PlaceholderBase(const CXXMethodDecl *MD) : ParamOrMethod(MD) {}
- const AccessPath &getAccessPath() const { return Path; }
- const Expr *getIssueExpr() const { return IssueExpr; }
+ const ParmVarDecl *getParmVarDecl() const {
+ return ParamOrMethod.dyn_cast<const ParmVarDecl *>();
+ }
- void dump(llvm::raw_ostream &OS) const override;
+ const CXXMethodDecl *getMethodDecl() const {
+ return ParamOrMethod.dyn_cast<const CXXMethodDecl *>();
+ }
- static bool classof(const Loan *L) { return L->getKind() == Kind::Path; }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(ParamOrMethod.getOpaqueValue());
+ }
};
-/// A placeholder loan held by a function parameter or an implicit 'this'
-/// object, representing a borrow from the caller's scope.
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable or a field within it: var.field.*
///
-/// Created at function entry for each pointer or reference parameter or for
-/// the implicit 'this' parameter of instance methods, 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)
+/// An AccessPath consists of:
+/// - A base: either a ValueDecl, MaterializeTemporaryExpr, or
PlaceholderBase
+/// - A sequence of PathElements representing field accesses or interior
+/// regions
///
-/// When a placeholder loan escapes the function (e.g., via return), it
-/// indicates the parameter or method should be marked
[[clang::lifetimebound]],
-/// enabling lifetime annotation suggestions.
-class PlaceholderLoan : public Loan {
- /// The function parameter or method (representing 'this') that holds this
- /// placeholder loan.
- llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *> ParamOrMethod;
+/// Examples:
+/// - `x` -> Base=x, Elements=[]
+/// - `x.field` -> Base=x, Elements=[.field]
+/// - `x.*` (e.g., string_view from string) -> Base=x, Elements=[.*]
+/// - `x.field.*` -> Base=x, Elements=[.field, .*]
+/// - `$param.field` -> Base=$param, Elements=[.field]
+///
+/// TODO: Model access paths of other types, e.g. heap and globals.
+class AccessPath {
+ /// The base of the access path: a variable, temporary, or placeholder.
+ const llvm::PointerUnion<const clang::ValueDecl *,
+ const clang::MaterializeTemporaryExpr *,
+ const PlaceholderBase *>
+ Base;
+ /// The path elements representing field accesses and access to unnamed
+ /// interior regions.
+ llvm::SmallVector<PathElement, 1> Elements;
public:
- PlaceholderLoan(LoanID ID, const ParmVarDecl *PVD)
- : Loan(Kind::Placeholder, ID), ParamOrMethod(PVD) {}
+ AccessPath(const clang::ValueDecl *D) : Base(D) {}
+ AccessPath(const clang::MaterializeTemporaryExpr *MTE) : Base(MTE) {}
+ AccessPath(const PlaceholderBase *PB) : Base(PB) {}
- PlaceholderLoan(LoanID ID, const CXXMethodDecl *MD)
- : Loan(Kind::Placeholder, ID), ParamOrMethod(MD) {}
+ /// Creates an extended access path by appending a path element.
+ /// Example: AccessPath(x_path, field) creates path to `x.field`.
+ AccessPath(const AccessPath &Other, PathElement E)
+ : Base(Other.Base), Elements(Other.Elements) {
+ Elements.push_back(E);
+ }
- const ParmVarDecl *getParmVarDecl() const {
- return ParamOrMethod.dyn_cast<const ParmVarDecl *>();
+ const clang::ValueDecl *getAsValueDecl() const {
+ return Base.dyn_cast<const clang::ValueDecl *>();
}
- const CXXMethodDecl *getMethodDecl() const {
- return ParamOrMethod.dyn_cast<const CXXMethodDecl *>();
+ const clang::MaterializeTemporaryExpr *getAsMaterializeTemporaryExpr() const
{
+ return Base.dyn_cast<const clang::MaterializeTemporaryExpr *>();
}
- void dump(llvm::raw_ostream &OS) const override;
+ const PlaceholderBase *getAsPlaceholderBase() const {
+ return Base.dyn_cast<const PlaceholderBase *>();
+ }
+
+ bool operator==(const AccessPath &RHS) const {
+ return Base == RHS.Base && Elements == RHS.Elements;
+ }
+
+ /// Returns true if this path is a prefix of Other (or same as Other).
+ /// Examples:
+ /// - `x` is a prefix of `x`, `x.field`, `x.field.*`
+ /// - `x.field` is a prefix of `x.field` and `x.field.nested`
+ /// - `x.field` is NOT a prefix of `x.other_field`
+ bool isPrefixOf(const AccessPath &Other) const {
+ if (Base != Other.Base || Elements.size() > Other.Elements.size())
+ return false;
+ for (size_t i = 0; i < Elements.size(); ++i)
----------------
Xazax-hun wrote:
This might be a hand-rolled `std::equal`?
https://github.com/llvm/llvm-project/pull/180369
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits