[llvm-branch-commits] [clang] [LifetimeSafety][NFC] Add field-labeled child edges to OriginNode and generalize subtree walks (PR #201510)

Gábor Horváth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jun 8 07:50:42 PDT 2026


================
@@ -66,47 +66,78 @@ struct Origin {
   }
 };
 
-/// A list of origins representing levels of indirection for pointer-like types.
+/// A tree of origins representing the structure of a pointer-like or
+/// record type.
 ///
-/// Each node in the list contains an OriginID representing a level of
-/// indirection. The list structure captures the multi-level nature of
-/// pointer and reference types in the lifetime analysis.
+/// Each node carries an OriginID and is connected to children via labeled
+/// edges: either a pointee edge (one level of pointer/reference indirection)
+/// or a field edge (a named field of a record). Pointer-like types form a
+/// pointee chain; record types fan out via field edges.
 ///
 /// Examples:
-///   - For `int& x`, the list has size 2:
+///   - For `int& x`, the chain has length 2:
 ///     * Outer: origin for the reference storage itself (the lvalue `x`)
 ///     * Inner: origin for what `x` refers to
 ///
-///   - For `int* p`, the list has size 2:
+///   - For `int* p`, the chain has length 2:
 ///     * Outer: origin for the pointer variable `p`
 ///     * Inner: origin for what `p` points to
 ///
-///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+///   - For `View v` (where View is gsl::Pointer), the chain has length 2:
 ///     * Outer: origin for the view object itself
 ///     * Inner: origin for what the view refers to
 ///
-///   - For `int** pp`, the list has size 3:
+///   - For `int** pp`, the chain has length 3:
 ///     * Outer: origin for `pp` itself
 ///     * Inner: origin for `*pp` (what `pp` points to)
 ///     * Inner->Inner: origin for `**pp` (what `*pp` points to)
 ///
-/// The list structure enables the analysis to track how loans flow through
-/// different levels of indirection when assignments and dereferences occur.
-///
-/// TODO: Currently list-shaped (each node has at most one pointee child).
-/// Will become tree-shaped once field children are added to support
-/// origin trees for records whose fields have origins.
+/// The structure enables the analysis to track how loans flow through
+/// levels of indirection and across record fields when assignments and
+/// dereferences occur.
 class OriginNode {
 public:
+  /// A labeled edge from this node to a child. The label distinguishes how
+  /// the child is reached: a null `FD` means a pointee edge (one level of
+  /// pointer/reference indirection); a non-null `FD` means a field edge
+  /// (the named field of a record). Putting the label on the edge lets
+  /// one child node play different roles per parent. For example, the subtree
+  /// for `s`'s `v` field is reached from `s`'s record (FD=v) and from
+  /// the lvalue outer built for the MemberExpr `s.v` (FD=null).
+  struct Edge {
+    const FieldDecl *FD;
+    OriginNode *Child;
+  };
+
   OriginNode(OriginID OID) : OID(OID) {}
 
+  OriginID getOriginID() const { return OID; }
+
+  llvm::ArrayRef<Edge> children() const { return Children; }
+
   OriginNode *getPointeeChild() const {
-    return Children.empty() ? nullptr : Children[0];
+    for (const Edge &E : Children)
+      if (!E.FD)
+        return E.Child;
+    return nullptr;
   }
 
-  OriginID getOriginID() const { return OID; }
+  OriginNode *getFieldChild(const FieldDecl *F) const {
+    assert(F);
+    for (const Edge &E : Children)
+      if (E.FD == F)
+        return E.Child;
+    return nullptr;
+  }
+
+  OriginNode *getFieldChildInChain(const FieldDecl *FD) const {
----------------
Xazax-hun wrote:

Why do we need this? Following arbitrary number of pointers along the way looks a bit scary to me. I wonder if the callers should be a bit more precise and tightly control every jump. 

https://github.com/llvm/llvm-project/pull/201510


More information about the llvm-branch-commits mailing list