[llvm-branch-commits] [clang] [LifetimeSafety] Track per-field origins for record types (PR #195603)

Gábor Horváth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jun 1 01:25:41 PDT 2026


================
@@ -49,27 +49,38 @@ bool FactsGenerator::hasOrigins(const Expr *E) const {
 /// Propagates origin information from Src to Dst through all levels of
 /// indirection, creating OriginFlowFacts at each level.
 ///
-/// This function enforces a critical type-safety invariant: both lists must
-/// have the same shape (same depth/structure). This invariant ensures that
-/// origins flow only between compatible types during expression evaluation.
+/// This function enforces a critical type-safety invariant: both trees
+/// must have the same pointee-chain depth, and field children are
+/// matched by `FieldDecl`. This invariant ensures that origins flow only
+/// between compatible types during expression evaluation. Field pairs
+/// found on both sides recurse; unmatched fields are skipped, which is
+/// exercised by `CK_DerivedToBase` flows where Base's and Derived's
+/// trees carry distinct direct-field FDs.
 ///
 /// Examples:
 ///   - `int* p = &x;` flows origins from `&x` (depth 1) to `p` (depth 1)
 ///   - `int** pp = &p;` flows origins from `&p` (depth 2) to `pp` (depth 2)
 ///     * Level 1: pp <- p's address
 ///     * Level 2: (*pp) <- what p points to (i.e., &x)
 ///   - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
+///   - `S s2 = s;` flows the top-level origin and recursively flows each
+///     matching `FieldDecl` subtree, so loans on `s.v.inner` propagate to
+///     `s2.v.inner`.
 void FactsGenerator::flow(OriginNode *Dst, OriginNode *Src, bool Kill) {
   if (!Dst)
     return;
   assert(Src &&
          "Dst is non-null but Src is null. List must have the same length");
   assert(Dst->getLength() == Src->getLength() &&
-         "Lists must have the same length");
+         "Pointee chains must have the same length");
 
   while (Dst && Src) {
     CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
         Dst->getOriginID(), Src->getOriginID(), Kill));
+    for (const OriginNode::Edge &E : Dst->children())
+      if (E.FD)
+        if (OriginNode *SrcF = Src->getFieldChild(E.FD))
+          flow(E.Child, SrcF, Kill);
----------------
Xazax-hun wrote:

Now with the lazy children and depth limits maybe this is actually fine as is. 

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


More information about the llvm-branch-commits mailing list