================
@@ -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

Reply via email to