[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
Fri Jun 12 02:11:26 PDT 2026


================
@@ -49,29 +49,45 @@ 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");
 
-  while (Dst && Src) {
-    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-        Dst->getOriginID(), Src->getOriginID(), Kill));
-    Dst = Dst->getPointeeChild();
-    Src = Src->getPointeeChild();
+  assert(Src && "Dst node has no paired Src node");
+
+  llvm::SmallVector<std::pair<OriginNode *, OriginNode *>, 4> Worklist{
+      {Dst, Src}};
+  for (size_t I = 0; I < Worklist.size(); ++I) {
+    auto [D, S] = Worklist[I];
----------------
Xazax-hun wrote:

Same question here about the queue vs always append approach.

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


More information about the llvm-branch-commits mailing list