Skip to content

Conversation

@usx95
Copy link
Contributor

@usx95 usx95 commented Sep 14, 2025

Add support for lifetimebound attributes in the lifetime safety analysis to track loans from function parameters to return values.

Implemented support for lifetimebound attributes on function parameters

This change replaces the single AssignOriginFact with two separate operations: OriginFlowFact and KillOriginFact. The key difference is in semantics:

  • Old AssignOriginFact: Replaced the destination origin's loans entirely with the source origin's loans.
  • New OriginFlowFact: Can now optionally merge the source origin's loans to the destination's existing loans.
  • New KillOriginFact: Clears all loans from an origin.

For function calls with lifetimebound parameters, we kill the the return value' origin first then use OriginFlowFact to accumulate loans from multiple parameters into the return value's origin - enabling tracking multiple lifetimebound arguments.

  • Added a new LifetimeAnnotations.h/cpp to provide helper functions for inspecting and inferring lifetime annotations
  • Moved several functions from CheckExprLifetime.cpp to the new file to make them reusable

The lifetimebound attribute is a key mechanism for expressing lifetime dependencies between function parameters and return values. This change enables the lifetime safety analysis to properly track these dependencies, allowing it to detect more potential dangling reference issues.

@usx95 usx95 changed the title lifetime-analysis-lifetimebound [LifetimeSafety] Implement support for lifetimebound attribute Sep 14, 2025
@github-actions
Copy link

github-actions bot commented Sep 14, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch 4 times, most recently from 29f3f38 to 076766a Compare September 21, 2025 16:32
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from 076766a to 1c079e3 Compare September 21, 2025 19:47
@usx95 usx95 changed the base branch from main to users/usx95/09-19-no-canonicalize September 21, 2025 19:47
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from 1c079e3 to 031309f Compare September 22, 2025 09:00
@usx95 usx95 marked this pull request as ready for review September 22, 2025 10:48
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer clang:analysis clang:temporal-safety Issue/FR relating to the lifetime analysis in Clang (-Wdangling, -Wreturn-local-addr) labels Sep 22, 2025
@usx95 usx95 requested a review from Xazax-hun September 22, 2025 10:49
@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2025

@llvm/pr-subscribers-llvm-adt
@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang-temporal-safety

Author: Utkarsh Saxena (usx95)

Changes

Implemented support for lifetimebound attributes on function parameters

This change replaces the single AssignOriginFact with two separate operations: OriginFlowFact and KillOriginFact. The key difference is in semantics:

  • Old AssignOriginFact: Replaced the destination origin's loans entirely with the source origin's loans.
  • New OriginFlowFact: Merges/adds the source origin's loans to the destination's existing loans.
  • New KillOriginFact: Clears all loans from an origin.

For assignments, the analysis now uses both operations in sequence (kill then flow) to maintain the original "replace" semantics. However, for function calls with lifetimebound parameters, it can use just OriginFlowFact to accumulate loans from multiple parameters into the return value's origin - enabling tracking multiple lifetimebound arguments.


Patch is 54.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158489.diff

6 Files Affected:

  • (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety.h (+6-5)
  • (modified) clang/lib/Analysis/LifetimeSafety.cpp (+149-50)
  • (modified) clang/test/Analysis/LifetimeSafety/benchmark.py (+1-1)
  • (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+79-76)
  • (modified) clang/test/Sema/warn-lifetime-safety.cpp (+149)
  • (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+255-2)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index 7e1bfc903083e..512cb76cd6349 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -75,13 +75,14 @@ template <typename Tag> struct ID {
   }
 };
 
-template <typename Tag>
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
-  return OS << ID.Value;
-}
-
 using LoanID = ID<struct LoanTag>;
 using OriginID = ID<struct OriginTag>;
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
+  return OS << ID.Value;
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
+  return OS << ID.Value;
+}
 
 // Using LLVM's immutable collections is efficient for dataflow analysis
 // as it avoids deep copies during state transitions.
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index da4af42853e55..a8bbeb6af78e2 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -212,8 +212,10 @@ class Fact {
     /// A loan expires as its underlying storage is freed (e.g., variable goes
     /// out of scope).
     Expire,
+    /// The loan set of an origin is cleared.
+    KillOrigin,
     /// An origin is propagated from a source to a destination (e.g., p = q).
-    AssignOrigin,
+    OriginFlow,
     /// An origin escapes the function by flowing into the return value.
     ReturnOfOrigin,
     /// An origin is used (eg. dereferencing a pointer).
@@ -285,22 +287,24 @@ class ExpireFact : public Fact {
   }
 };
 
-class AssignOriginFact : public Fact {
+class OriginFlowFact : public Fact {
   OriginID OIDDest;
   OriginID OIDSrc;
 
 public:
   static bool classof(const Fact *F) {
-    return F->getKind() == Kind::AssignOrigin;
+    return F->getKind() == Kind::OriginFlow;
   }
 
-  AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)
-      : Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
+  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc)
+      : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
+
   OriginID getDestOriginID() const { return OIDDest; }
   OriginID getSrcOriginID() const { return OIDSrc; }
+
   void dump(llvm::raw_ostream &OS, const LoanManager &,
             const OriginManager &OM) const override {
-    OS << "AssignOrigin (Dest: ";
+    OS << "OriginFlow (Dest: ";
     OM.dump(getDestOriginID(), OS);
     OS << ", Src: ";
     OM.dump(getSrcOriginID(), OS);
@@ -353,6 +357,23 @@ class UseFact : public Fact {
   }
 };
 
+class KillOriginFact : public Fact {
+  OriginID OID;
+
+public:
+  static bool classof(const Fact *F) {
+    return F->getKind() == Kind::KillOrigin;
+  }
+  KillOriginFact(OriginID OID) : Fact(Kind::KillOrigin), OID(OID) {}
+  OriginID getOriginID() const { return OID; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override {
+    OS << "KillOrigin (";
+    OM.dump(getOriginID(), OS);
+    OS << ")\n";
+  }
+};
 /// A dummy-fact used to mark a specific point in the code for testing.
 /// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
 class TestPointFact : public Fact {
@@ -453,8 +474,10 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     for (const Decl *D : DS->decls())
       if (const auto *VD = dyn_cast<VarDecl>(D))
         if (hasOrigin(VD))
-          if (const Expr *InitExpr = VD->getInit())
-            addAssignOriginFact(*VD, *InitExpr);
+          if (const Expr *InitExpr = VD->getInit()) {
+            killOrigin(VD);
+            addOriginFlowFact(*VD, *InitExpr);
+          }
   }
 
   void VisitDeclRefExpr(const DeclRefExpr *DRE) {
@@ -492,9 +515,23 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
         isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
       // The argument is the implicit object itself.
       handleFunctionCall(MCE, MCE->getMethodDecl(),
-                         {MCE->getImplicitObjectArgument()});
+                         {MCE->getImplicitObjectArgument()},
+                         /*IsGslConstruction=*/true);
     }
-    // FIXME: A more general VisitCallExpr could also be used here.
+    if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
+      // Construct the argument list, with the implicit 'this' object as the
+      // first argument.
+      llvm::SmallVector<const Expr *, 4> Args;
+      Args.push_back(MCE->getImplicitObjectArgument());
+      Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+
+      handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
+    }
+  }
+
+  void VisitCallExpr(const CallExpr *CE) {
+    handleFunctionCall(CE, CE->getDirectCallee(),
+                       {CE->getArgs(), CE->getNumArgs()});
   }
 
   void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
@@ -508,7 +545,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       return;
     // An ImplicitCastExpr node itself gets an origin, which flows from the
     // origin of its sub-expression (after stripping its own parens/casts).
-    addAssignOriginFact(*ICE, *ICE->getSubExpr());
+    addOriginFlowFact(*ICE, *ICE->getSubExpr());
   }
 
   void VisitUnaryOperator(const UnaryOperator *UO) {
@@ -522,7 +559,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       // its sub-expression (x). This fact will cause the dataflow analysis
       // to propagate any loans held by the sub-expression's origin to the
       // origin of this UnaryOperator expression.
-      addAssignOriginFact(*UO, *SubExpr);
+      addOriginFlowFact(*UO, *SubExpr);
     }
   }
 
@@ -542,8 +579,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
   }
 
   void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
-    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2)
+    // Assignment operators have special "kill-then-propagate" semantics
+    // and are handled separately.
+    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
       handleAssignment(OCE->getArg(0), OCE->getArg(1));
+      return;
+    }
+    handleFunctionCall(OCE, OCE->getDirectCallee(),
+                       {OCE->getArgs(), OCE->getNumArgs()},
+                       /*IsGslConstruction=*/false);
   }
 
   void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
@@ -552,7 +596,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (handleTestPoint(FCE))
       return;
     if (isGslPointerType(FCE->getType()))
-      addAssignOriginFact(*FCE, *FCE->getSubExpr());
+      addOriginFlowFact(*FCE, *FCE->getSubExpr());
   }
 
   void VisitInitListExpr(const InitListExpr *ILE) {
@@ -561,7 +605,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     // For list initialization with a single element, like `View{...}`, the
     // origin of the list itself is the origin of its single element.
     if (ILE->getNumInits() == 1)
-      addAssignOriginFact(*ILE, *ILE->getInit(0));
+      addOriginFlowFact(*ILE, *ILE->getInit(0));
   }
 
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE) {
@@ -569,7 +613,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       return;
     // A temporary object's origin is the same as the origin of the
     // expression that initializes it.
-    addAssignOriginFact(*MTE, *MTE->getSubExpr());
+    addOriginFlowFact(*MTE, *MTE->getSubExpr());
   }
 
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
@@ -624,34 +668,68 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (CCE->getNumArgs() != 1)
       return;
     if (hasOrigin(CCE->getArg(0)))
-      addAssignOriginFact(*CCE, *CCE->getArg(0));
+      addOriginFlowFact(*CCE, *CCE->getArg(0));
     else
       // This could be a new borrow.
       handleFunctionCall(CCE, CCE->getConstructor(),
-                         {CCE->getArgs(), CCE->getNumArgs()});
+                         {CCE->getArgs(), CCE->getNumArgs()},
+                         /*IsGslConstruction=*/true);
+  }
+  static const FunctionDecl *
+  getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
+    return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
   }
 
+  static const CXXMethodDecl *
+  getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
+    const FunctionDecl *FD = CMD;
+    return cast_if_present<CXXMethodDecl>(
+        getDeclWithMergedLifetimeBoundAttrs(FD));
+  }
+  static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
+    FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+    const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
+    if (!TSI)
+      return false;
+    // Don't declare this variable in the second operand of the for-statement;
+    // GCC miscompiles that by ending its lifetime before evaluating the
+    // third operand. See gcc.gnu.org/PR86769.
+    AttributedTypeLoc ATL;
+    for (TypeLoc TL = TSI->getTypeLoc();
+         (ATL = TL.getAsAdjusted<AttributedTypeLoc>());
+         TL = ATL.getModifiedLoc()) {
+      if (ATL.getAttrAs<LifetimeBoundAttr>())
+        return true;
+    }
+    return false;
+  }
   /// Checks if a call-like expression creates a borrow by passing a value to a
   /// reference parameter, creating an IssueFact if it does.
   void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
-                          ArrayRef<const Expr *> Args) {
-    if (!FD)
+                          ArrayRef<const Expr *> Args,
+                          bool IsGslConstruction = false) {
+    // Ignore functions returning values with no origin.
+    if (!FD || !hasOrigin(Call))
       return;
-    // TODO: Handle more than one arguments.
-    for (unsigned I = 0; I <= 0 /*Args.size()*/; ++I) {
-      const Expr *ArgExpr = Args[I];
-
-      // Propagate origins for CXX this.
-      if (FD->isCXXClassMember() && I == 0) {
-        addAssignOriginFact(*Call, *ArgExpr);
-        continue;
-      }
-      // The parameter is a pointer, reference, or gsl::Pointer.
-      // This is a borrow. We propagate the origin from the argument expression
-      // at the call site to the parameter declaration in the callee.
-      if (hasOrigin(ArgExpr))
-        addAssignOriginFact(*Call, *ArgExpr);
-    }
+    auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
+      const ParmVarDecl *PVD = nullptr;
+      if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+          Method && Method->isInstance()) {
+        if (I == 0)
+          // For the 'this' argument, the attribute is on the method itself.
+          return implicitObjectParamIsLifetimeBound(Method);
+        if ((I - 1) < Method->getNumParams())
+          // For explicit arguments, find the corresponding parameter
+          // declaration.
+          PVD = Method->getParamDecl(I - 1);
+      } else if (I < FD->getNumParams())
+        // For free functions or static methods.
+        PVD = FD->getParamDecl(I);
+      return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
+    };
+    for (unsigned I = 0; I < Args.size(); ++I)
+      if (IsGslConstruction || IsArgLifetimeBound(I))
+        addOriginFlowFact(*Call, *Args[I]);
   }
 
   /// Creates a loan for the storage path of a given declaration reference.
@@ -668,11 +746,16 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
   }
 
   template <typename Destination, typename Source>
-  void addAssignOriginFact(const Destination &D, const Source &S) {
+  void addOriginFlowFact(const Destination &D, const Source &S) {
     OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
     OriginID SrcOID = FactMgr.getOriginMgr().get(S);
     CurrentBlockFacts.push_back(
-        FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
+        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID));
+  }
+
+  void killOrigin(const ValueDecl *D) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(*D);
+    CurrentBlockFacts.push_back(FactMgr.createFact<KillOriginFact>(DestOID));
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
@@ -703,12 +786,12 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (const auto *DRE_LHS =
             dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
       markUseAsWrite(DRE_LHS);
-      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
-        // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`.
-        // LHS must be a pointer/reference type that can be an origin. RHS must
-        // also represent an origin (either another pointer/ref or an
-        // address-of).
-        addAssignOriginFact(*VD_LHS, *RHSExpr);
+      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
+        // Kill the old loans of the destination origin and flow the new loans
+        // from the source origin.
+        killOrigin(VD_LHS);
+        addOriginFlowFact(*VD_LHS, *RHSExpr);
+      }
     }
   }
 
@@ -882,8 +965,10 @@ class DataflowAnalysis {
       return D->transfer(In, *F->getAs<IssueFact>());
     case Fact::Kind::Expire:
       return D->transfer(In, *F->getAs<ExpireFact>());
-    case Fact::Kind::AssignOrigin:
-      return D->transfer(In, *F->getAs<AssignOriginFact>());
+    case Fact::Kind::OriginFlow:
+      return D->transfer(In, *F->getAs<OriginFlowFact>());
+    case Fact::Kind::KillOrigin:
+      return D->transfer(In, *F->getAs<KillOriginFact>());
     case Fact::Kind::ReturnOfOrigin:
       return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
     case Fact::Kind::Use:
@@ -897,7 +982,8 @@ class DataflowAnalysis {
 public:
   Lattice transfer(Lattice In, const IssueFact &) { return In; }
   Lattice transfer(Lattice In, const ExpireFact &) { return In; }
-  Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
+  Lattice transfer(Lattice In, const OriginFlowFact &) { return In; }
+  Lattice transfer(Lattice In, const KillOriginFact &) { return In; }
   Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
   Lattice transfer(Lattice In, const UseFact &) { return In; }
   Lattice transfer(Lattice In, const TestPointFact &) { return In; }
@@ -1049,14 +1135,27 @@ class LoanPropagationAnalysis
         LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
   }
 
-  /// The destination origin's loan set is replaced by the source's.
-  /// This implicitly "resets" the old loans of the destination.
-  Lattice transfer(Lattice In, const AssignOriginFact &F) {
+  /// A flow from source to destination adds the source's loans to the
+  /// destination's, without clearing the destination's existing loans.
+  Lattice transfer(Lattice In, const OriginFlowFact &F) {
     OriginID DestOID = F.getDestOriginID();
     OriginID SrcOID = F.getSrcOriginID();
+
+    LoanSet DestLoans = getLoans(In, DestOID);
     LoanSet SrcLoans = getLoans(In, SrcOID);
+    LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
+
     return LoanPropagationLattice(
-        OriginLoanMapFactory.add(In.Origins, DestOID, SrcLoans));
+        OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
+  }
+
+  /// Clears the loan set of the specified origin. This is used on the
+  /// left-hand side of an assignment to invalidate the variable's old lifetime.
+  Lattice transfer(Lattice In, const KillOriginFact &F) {
+    OriginID OID = F.getOriginID();
+    // Replace the origin's loan set with an empty set.
+    return LoanPropagationLattice(OriginLoanMapFactory.add(
+        In.Origins, OID, LoanSetFactory.getEmptySet()));
   }
 
   LoanSet getLoans(OriginID OID, ProgramPoint P) {
diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py
index 2373f9984eecd..d2e5f0b2122a3 100644
--- a/clang/test/Analysis/LifetimeSafety/benchmark.py
+++ b/clang/test/Analysis/LifetimeSafety/benchmark.py
@@ -340,7 +340,7 @@ def run_single_test(
             "name": "cycle",
             "title": "Pointer Cycle in Loop",
             "generator_func": generate_cpp_cycle_test,
-            "n_values": [25, 50, 75, 100],
+            "n_values": [50, 75, 100, 200, 300],
         },
         {
             "name": "merge",
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 7dac27506fb6b..910b2df73b2d5 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -12,12 +12,12 @@ MyObj* return_local_addr() {
   MyObj x {10};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
-// CHECK:   AssignOrigin (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
+// CHECK:   OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
   MyObj* p = &x;
-// CHECK:   AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
+// CHECK:   OriginFlow (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
   return p;
 // CHECK:   Use ([[O_P]] (Decl: p), Read)
-// CHECK:   AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
+// CHECK:   OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
 // CHECK:   ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
 // CHECK:   Expire ([[L_X]] (Path: x))
 }
@@ -29,26 +29,26 @@ MyObj* assign_and_return_local_addr() {
   MyObj y{20};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr))
-// CHECK:   AssignOrigin (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
+// CHECK:   OriginFlow (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
   MyObj* ptr1 = &y;
-// CHECK:   AssignOrigin (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
+// CHECK:   OriginFlow (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
   MyObj* ptr2 = ptr1;
 // CHECK:   Use ([[O_PTR1]] (Decl: ptr1), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
-// CHECK:   AssignOrigin (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK:   OriginFlow (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
   ptr2 = ptr1;
 // CHECK:   Use ([[O_PTR1]] (Decl: ptr1), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK:   OriginFlow (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
 // CHECK:   Use ({{[0-9]+}} (Decl: ptr2), Write)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
   ptr2 = ptr2; // Self assignment.
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK:   OriginFlow (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Write)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
   return ptr2;
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Read)
-// CHECK:   AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK:   OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
 // CHECK:   ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
 // CHECK:   Expire ([[L_Y]] (Path: y))
 }
@@ -70,9 +70,9 @@ void loan_expires_cpp() {
   MyObj obj{1};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_O...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2025

@llvm/pr-subscribers-clang-analysis

Author: Utkarsh Saxena (usx95)

Changes

Implemented support for lifetimebound attributes on function parameters

This change replaces the single AssignOriginFact with two separate operations: OriginFlowFact and KillOriginFact. The key difference is in semantics:

  • Old AssignOriginFact: Replaced the destination origin's loans entirely with the source origin's loans.
  • New OriginFlowFact: Merges/adds the source origin's loans to the destination's existing loans.
  • New KillOriginFact: Clears all loans from an origin.

For assignments, the analysis now uses both operations in sequence (kill then flow) to maintain the original "replace" semantics. However, for function calls with lifetimebound parameters, it can use just OriginFlowFact to accumulate loans from multiple parameters into the return value's origin - enabling tracking multiple lifetimebound arguments.


Patch is 54.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158489.diff

6 Files Affected:

  • (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety.h (+6-5)
  • (modified) clang/lib/Analysis/LifetimeSafety.cpp (+149-50)
  • (modified) clang/test/Analysis/LifetimeSafety/benchmark.py (+1-1)
  • (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+79-76)
  • (modified) clang/test/Sema/warn-lifetime-safety.cpp (+149)
  • (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+255-2)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index 7e1bfc903083e..512cb76cd6349 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -75,13 +75,14 @@ template <typename Tag> struct ID {
   }
 };
 
-template <typename Tag>
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
-  return OS << ID.Value;
-}
-
 using LoanID = ID<struct LoanTag>;
 using OriginID = ID<struct OriginTag>;
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
+  return OS << ID.Value;
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
+  return OS << ID.Value;
+}
 
 // Using LLVM's immutable collections is efficient for dataflow analysis
 // as it avoids deep copies during state transitions.
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index da4af42853e55..a8bbeb6af78e2 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -212,8 +212,10 @@ class Fact {
     /// A loan expires as its underlying storage is freed (e.g., variable goes
     /// out of scope).
     Expire,
+    /// The loan set of an origin is cleared.
+    KillOrigin,
     /// An origin is propagated from a source to a destination (e.g., p = q).
-    AssignOrigin,
+    OriginFlow,
     /// An origin escapes the function by flowing into the return value.
     ReturnOfOrigin,
     /// An origin is used (eg. dereferencing a pointer).
@@ -285,22 +287,24 @@ class ExpireFact : public Fact {
   }
 };
 
-class AssignOriginFact : public Fact {
+class OriginFlowFact : public Fact {
   OriginID OIDDest;
   OriginID OIDSrc;
 
 public:
   static bool classof(const Fact *F) {
-    return F->getKind() == Kind::AssignOrigin;
+    return F->getKind() == Kind::OriginFlow;
   }
 
-  AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)
-      : Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
+  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc)
+      : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
+
   OriginID getDestOriginID() const { return OIDDest; }
   OriginID getSrcOriginID() const { return OIDSrc; }
+
   void dump(llvm::raw_ostream &OS, const LoanManager &,
             const OriginManager &OM) const override {
-    OS << "AssignOrigin (Dest: ";
+    OS << "OriginFlow (Dest: ";
     OM.dump(getDestOriginID(), OS);
     OS << ", Src: ";
     OM.dump(getSrcOriginID(), OS);
@@ -353,6 +357,23 @@ class UseFact : public Fact {
   }
 };
 
+class KillOriginFact : public Fact {
+  OriginID OID;
+
+public:
+  static bool classof(const Fact *F) {
+    return F->getKind() == Kind::KillOrigin;
+  }
+  KillOriginFact(OriginID OID) : Fact(Kind::KillOrigin), OID(OID) {}
+  OriginID getOriginID() const { return OID; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override {
+    OS << "KillOrigin (";
+    OM.dump(getOriginID(), OS);
+    OS << ")\n";
+  }
+};
 /// A dummy-fact used to mark a specific point in the code for testing.
 /// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
 class TestPointFact : public Fact {
@@ -453,8 +474,10 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     for (const Decl *D : DS->decls())
       if (const auto *VD = dyn_cast<VarDecl>(D))
         if (hasOrigin(VD))
-          if (const Expr *InitExpr = VD->getInit())
-            addAssignOriginFact(*VD, *InitExpr);
+          if (const Expr *InitExpr = VD->getInit()) {
+            killOrigin(VD);
+            addOriginFlowFact(*VD, *InitExpr);
+          }
   }
 
   void VisitDeclRefExpr(const DeclRefExpr *DRE) {
@@ -492,9 +515,23 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
         isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
       // The argument is the implicit object itself.
       handleFunctionCall(MCE, MCE->getMethodDecl(),
-                         {MCE->getImplicitObjectArgument()});
+                         {MCE->getImplicitObjectArgument()},
+                         /*IsGslConstruction=*/true);
     }
-    // FIXME: A more general VisitCallExpr could also be used here.
+    if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
+      // Construct the argument list, with the implicit 'this' object as the
+      // first argument.
+      llvm::SmallVector<const Expr *, 4> Args;
+      Args.push_back(MCE->getImplicitObjectArgument());
+      Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+
+      handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
+    }
+  }
+
+  void VisitCallExpr(const CallExpr *CE) {
+    handleFunctionCall(CE, CE->getDirectCallee(),
+                       {CE->getArgs(), CE->getNumArgs()});
   }
 
   void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
@@ -508,7 +545,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       return;
     // An ImplicitCastExpr node itself gets an origin, which flows from the
     // origin of its sub-expression (after stripping its own parens/casts).
-    addAssignOriginFact(*ICE, *ICE->getSubExpr());
+    addOriginFlowFact(*ICE, *ICE->getSubExpr());
   }
 
   void VisitUnaryOperator(const UnaryOperator *UO) {
@@ -522,7 +559,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       // its sub-expression (x). This fact will cause the dataflow analysis
       // to propagate any loans held by the sub-expression's origin to the
       // origin of this UnaryOperator expression.
-      addAssignOriginFact(*UO, *SubExpr);
+      addOriginFlowFact(*UO, *SubExpr);
     }
   }
 
@@ -542,8 +579,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
   }
 
   void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
-    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2)
+    // Assignment operators have special "kill-then-propagate" semantics
+    // and are handled separately.
+    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
       handleAssignment(OCE->getArg(0), OCE->getArg(1));
+      return;
+    }
+    handleFunctionCall(OCE, OCE->getDirectCallee(),
+                       {OCE->getArgs(), OCE->getNumArgs()},
+                       /*IsGslConstruction=*/false);
   }
 
   void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
@@ -552,7 +596,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (handleTestPoint(FCE))
       return;
     if (isGslPointerType(FCE->getType()))
-      addAssignOriginFact(*FCE, *FCE->getSubExpr());
+      addOriginFlowFact(*FCE, *FCE->getSubExpr());
   }
 
   void VisitInitListExpr(const InitListExpr *ILE) {
@@ -561,7 +605,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     // For list initialization with a single element, like `View{...}`, the
     // origin of the list itself is the origin of its single element.
     if (ILE->getNumInits() == 1)
-      addAssignOriginFact(*ILE, *ILE->getInit(0));
+      addOriginFlowFact(*ILE, *ILE->getInit(0));
   }
 
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE) {
@@ -569,7 +613,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
       return;
     // A temporary object's origin is the same as the origin of the
     // expression that initializes it.
-    addAssignOriginFact(*MTE, *MTE->getSubExpr());
+    addOriginFlowFact(*MTE, *MTE->getSubExpr());
   }
 
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
@@ -624,34 +668,68 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (CCE->getNumArgs() != 1)
       return;
     if (hasOrigin(CCE->getArg(0)))
-      addAssignOriginFact(*CCE, *CCE->getArg(0));
+      addOriginFlowFact(*CCE, *CCE->getArg(0));
     else
       // This could be a new borrow.
       handleFunctionCall(CCE, CCE->getConstructor(),
-                         {CCE->getArgs(), CCE->getNumArgs()});
+                         {CCE->getArgs(), CCE->getNumArgs()},
+                         /*IsGslConstruction=*/true);
+  }
+  static const FunctionDecl *
+  getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
+    return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
   }
 
+  static const CXXMethodDecl *
+  getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
+    const FunctionDecl *FD = CMD;
+    return cast_if_present<CXXMethodDecl>(
+        getDeclWithMergedLifetimeBoundAttrs(FD));
+  }
+  static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
+    FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+    const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
+    if (!TSI)
+      return false;
+    // Don't declare this variable in the second operand of the for-statement;
+    // GCC miscompiles that by ending its lifetime before evaluating the
+    // third operand. See gcc.gnu.org/PR86769.
+    AttributedTypeLoc ATL;
+    for (TypeLoc TL = TSI->getTypeLoc();
+         (ATL = TL.getAsAdjusted<AttributedTypeLoc>());
+         TL = ATL.getModifiedLoc()) {
+      if (ATL.getAttrAs<LifetimeBoundAttr>())
+        return true;
+    }
+    return false;
+  }
   /// Checks if a call-like expression creates a borrow by passing a value to a
   /// reference parameter, creating an IssueFact if it does.
   void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
-                          ArrayRef<const Expr *> Args) {
-    if (!FD)
+                          ArrayRef<const Expr *> Args,
+                          bool IsGslConstruction = false) {
+    // Ignore functions returning values with no origin.
+    if (!FD || !hasOrigin(Call))
       return;
-    // TODO: Handle more than one arguments.
-    for (unsigned I = 0; I <= 0 /*Args.size()*/; ++I) {
-      const Expr *ArgExpr = Args[I];
-
-      // Propagate origins for CXX this.
-      if (FD->isCXXClassMember() && I == 0) {
-        addAssignOriginFact(*Call, *ArgExpr);
-        continue;
-      }
-      // The parameter is a pointer, reference, or gsl::Pointer.
-      // This is a borrow. We propagate the origin from the argument expression
-      // at the call site to the parameter declaration in the callee.
-      if (hasOrigin(ArgExpr))
-        addAssignOriginFact(*Call, *ArgExpr);
-    }
+    auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
+      const ParmVarDecl *PVD = nullptr;
+      if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+          Method && Method->isInstance()) {
+        if (I == 0)
+          // For the 'this' argument, the attribute is on the method itself.
+          return implicitObjectParamIsLifetimeBound(Method);
+        if ((I - 1) < Method->getNumParams())
+          // For explicit arguments, find the corresponding parameter
+          // declaration.
+          PVD = Method->getParamDecl(I - 1);
+      } else if (I < FD->getNumParams())
+        // For free functions or static methods.
+        PVD = FD->getParamDecl(I);
+      return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
+    };
+    for (unsigned I = 0; I < Args.size(); ++I)
+      if (IsGslConstruction || IsArgLifetimeBound(I))
+        addOriginFlowFact(*Call, *Args[I]);
   }
 
   /// Creates a loan for the storage path of a given declaration reference.
@@ -668,11 +746,16 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
   }
 
   template <typename Destination, typename Source>
-  void addAssignOriginFact(const Destination &D, const Source &S) {
+  void addOriginFlowFact(const Destination &D, const Source &S) {
     OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
     OriginID SrcOID = FactMgr.getOriginMgr().get(S);
     CurrentBlockFacts.push_back(
-        FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
+        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID));
+  }
+
+  void killOrigin(const ValueDecl *D) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(*D);
+    CurrentBlockFacts.push_back(FactMgr.createFact<KillOriginFact>(DestOID));
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
@@ -703,12 +786,12 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
     if (const auto *DRE_LHS =
             dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
       markUseAsWrite(DRE_LHS);
-      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
-        // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`.
-        // LHS must be a pointer/reference type that can be an origin. RHS must
-        // also represent an origin (either another pointer/ref or an
-        // address-of).
-        addAssignOriginFact(*VD_LHS, *RHSExpr);
+      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
+        // Kill the old loans of the destination origin and flow the new loans
+        // from the source origin.
+        killOrigin(VD_LHS);
+        addOriginFlowFact(*VD_LHS, *RHSExpr);
+      }
     }
   }
 
@@ -882,8 +965,10 @@ class DataflowAnalysis {
       return D->transfer(In, *F->getAs<IssueFact>());
     case Fact::Kind::Expire:
       return D->transfer(In, *F->getAs<ExpireFact>());
-    case Fact::Kind::AssignOrigin:
-      return D->transfer(In, *F->getAs<AssignOriginFact>());
+    case Fact::Kind::OriginFlow:
+      return D->transfer(In, *F->getAs<OriginFlowFact>());
+    case Fact::Kind::KillOrigin:
+      return D->transfer(In, *F->getAs<KillOriginFact>());
     case Fact::Kind::ReturnOfOrigin:
       return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
     case Fact::Kind::Use:
@@ -897,7 +982,8 @@ class DataflowAnalysis {
 public:
   Lattice transfer(Lattice In, const IssueFact &) { return In; }
   Lattice transfer(Lattice In, const ExpireFact &) { return In; }
-  Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
+  Lattice transfer(Lattice In, const OriginFlowFact &) { return In; }
+  Lattice transfer(Lattice In, const KillOriginFact &) { return In; }
   Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
   Lattice transfer(Lattice In, const UseFact &) { return In; }
   Lattice transfer(Lattice In, const TestPointFact &) { return In; }
@@ -1049,14 +1135,27 @@ class LoanPropagationAnalysis
         LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
   }
 
-  /// The destination origin's loan set is replaced by the source's.
-  /// This implicitly "resets" the old loans of the destination.
-  Lattice transfer(Lattice In, const AssignOriginFact &F) {
+  /// A flow from source to destination adds the source's loans to the
+  /// destination's, without clearing the destination's existing loans.
+  Lattice transfer(Lattice In, const OriginFlowFact &F) {
     OriginID DestOID = F.getDestOriginID();
     OriginID SrcOID = F.getSrcOriginID();
+
+    LoanSet DestLoans = getLoans(In, DestOID);
     LoanSet SrcLoans = getLoans(In, SrcOID);
+    LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
+
     return LoanPropagationLattice(
-        OriginLoanMapFactory.add(In.Origins, DestOID, SrcLoans));
+        OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
+  }
+
+  /// Clears the loan set of the specified origin. This is used on the
+  /// left-hand side of an assignment to invalidate the variable's old lifetime.
+  Lattice transfer(Lattice In, const KillOriginFact &F) {
+    OriginID OID = F.getOriginID();
+    // Replace the origin's loan set with an empty set.
+    return LoanPropagationLattice(OriginLoanMapFactory.add(
+        In.Origins, OID, LoanSetFactory.getEmptySet()));
   }
 
   LoanSet getLoans(OriginID OID, ProgramPoint P) {
diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py
index 2373f9984eecd..d2e5f0b2122a3 100644
--- a/clang/test/Analysis/LifetimeSafety/benchmark.py
+++ b/clang/test/Analysis/LifetimeSafety/benchmark.py
@@ -340,7 +340,7 @@ def run_single_test(
             "name": "cycle",
             "title": "Pointer Cycle in Loop",
             "generator_func": generate_cpp_cycle_test,
-            "n_values": [25, 50, 75, 100],
+            "n_values": [50, 75, 100, 200, 300],
         },
         {
             "name": "merge",
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 7dac27506fb6b..910b2df73b2d5 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -12,12 +12,12 @@ MyObj* return_local_addr() {
   MyObj x {10};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
-// CHECK:   AssignOrigin (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
+// CHECK:   OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
   MyObj* p = &x;
-// CHECK:   AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
+// CHECK:   OriginFlow (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
   return p;
 // CHECK:   Use ([[O_P]] (Decl: p), Read)
-// CHECK:   AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
+// CHECK:   OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
 // CHECK:   ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
 // CHECK:   Expire ([[L_X]] (Path: x))
 }
@@ -29,26 +29,26 @@ MyObj* assign_and_return_local_addr() {
   MyObj y{20};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr))
-// CHECK:   AssignOrigin (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
+// CHECK:   OriginFlow (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
   MyObj* ptr1 = &y;
-// CHECK:   AssignOrigin (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
+// CHECK:   OriginFlow (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
   MyObj* ptr2 = ptr1;
 // CHECK:   Use ([[O_PTR1]] (Decl: ptr1), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
-// CHECK:   AssignOrigin (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK:   OriginFlow (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
   ptr2 = ptr1;
 // CHECK:   Use ([[O_PTR1]] (Decl: ptr1), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK:   OriginFlow (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
 // CHECK:   Use ({{[0-9]+}} (Decl: ptr2), Write)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
   ptr2 = ptr2; // Self assignment.
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Read)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK:   OriginFlow (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Write)
-// CHECK:   AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK:   OriginFlow (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
   return ptr2;
 // CHECK:   Use ([[O_PTR2]] (Decl: ptr2), Read)
-// CHECK:   AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK:   OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
 // CHECK:   ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
 // CHECK:   Expire ([[L_Y]] (Path: y))
 }
@@ -70,9 +70,9 @@ void loan_expires_cpp() {
   MyObj obj{1};
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_O...
[truncated]

@usx95 usx95 requested review from jvoung and ymand September 22, 2025 14:25
@usx95 usx95 force-pushed the users/usx95/09-19-no-canonicalize branch from 0775d9e to 53adfd8 Compare September 22, 2025 15:33
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch 2 times, most recently from 4e63e10 to 271e174 Compare September 22, 2025 21:32
@llvmbot llvmbot added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Sep 22, 2025
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from 271e174 to caac182 Compare September 23, 2025 11:58
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from caac182 to 082d8a2 Compare September 23, 2025 15:05
@usx95 usx95 requested a review from Xazax-hun September 23, 2025 15:11
Copy link
Collaborator

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, some minor nits inline. Thanks!

@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from 082d8a2 to 1c92d1c Compare September 23, 2025 17:06
Base automatically changed from users/usx95/09-19-no-canonicalize to main September 25, 2025 16:11
@usx95 usx95 force-pushed the users/usx95/09-14-lifetime-analysis-lifetimebound branch from 382322b to 335b2b5 Compare September 25, 2025 22:24
@usx95 usx95 enabled auto-merge (squash) September 25, 2025 22:26
@usx95 usx95 merged commit 0e17fcf into main Sep 25, 2025
9 checks passed
@usx95 usx95 deleted the users/usx95/09-14-lifetime-analysis-lifetimebound branch September 25, 2025 22:44
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 26, 2025

LLVM Buildbot has detected a new failure on builder ppc64le-lld-multistage-test running on ppc64le-lld-multistage-test while building clang at step 12 "build-stage2-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/168/builds/16348

Here is the relevant piece of the build log for the reference
Step 12 (build-stage2-unified-tree) failure: build (failure) (timed out)
...
2041.442 [56/10/6647] Linking CXX executable bin/opt
2041.762 [56/9/6648] Linking CXX executable tools/lld/unittests/AsLibAll/LLDAsLibAllTests
2041.823 [56/8/6649] Linking CXX executable bin/clang-sycl-linker
2041.948 [56/7/6650] Linking CXX executable bin/llvm-gsymutil
2041.987 [56/6/6651] Linking CXX executable bin/llvm-lto2
2042.005 [56/5/6652] Linking CXX executable bin/llvm-opt-fuzzer
2042.084 [56/4/6653] Linking CXX executable unittests/MIR/MIRTests
2042.737 [56/3/6654] Linking CXX executable unittests/MI/MITests
2078.234 [56/2/6655] Building CXX object tools/bugpoint-passes/CMakeFiles/BugpointPasses.dir/TestPasses.cpp.o
2078.872 [55/2/6656] Linking CXX shared module lib/BugpointPasses.so
command timed out: 1200 seconds without output running [b'ninja'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=3279.432294

@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 26, 2025

LLVM Buildbot has detected a new failure on builder sanitizer-ppc64le-linux running on ppc64le-sanitizer while building clang at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/72/builds/15226

Here is the relevant piece of the build log for the reference
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/llvm/utils/lit/lit/discovery.py:276: warning: input '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/Unit' contained no tests
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
Testing:  0.. 10.. 20.. 30.. 40.. 50.
FAIL: SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (2953 of 5239)
******************** TEST 'SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=memory  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:17: int main(): Assertion `res == 0 || res == ENOENT' failed.
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 83489 Aborted                 /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 
FAIL: SanitizerCommon-asan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (3155 of 5239)
******************** TEST 'SanitizerCommon-asan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=address  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=address -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:17: int main(): Assertion `res == 0 || res == ENOENT' failed.
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 80153 Aborted                 /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70
FAIL: SanitizerCommon-ubsan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (3910 of 5239)
******************** TEST 'SanitizerCommon-ubsan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=undefined  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/ubsan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/ubsan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
Step 14 (test standalone compiler-rt) failure: test standalone compiler-rt (failure)
...
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/llvm/utils/lit/lit/discovery.py:276: warning: input '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/Unit' contained no tests
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
llvm-lit: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60: warning: Path reported by clang does not exist: "/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/powerpc64le-unknown-linux-gnu". This path was found by running ['/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang', '--target=powerpc64le-unknown-linux-gnu', '-m64', '-fno-function-sections', '-nobuiltininc', '-I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include', '-idirafter', '/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include', '-resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build', '-Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux', '-print-runtime-dir'].
Testing:  0.. 10.. 20.. 30.. 40.. 50.
FAIL: SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (2953 of 5239)
******************** TEST 'SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=memory  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:17: int main(): Assertion `res == 0 || res == ENOENT' failed.
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 83489 Aborted                 /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 
FAIL: SanitizerCommon-asan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (3155 of 5239)
******************** TEST 'SanitizerCommon-asan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=address  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=address -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:17: int main(): Assertion `res == 0 || res == ENOENT' failed.
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 80153 Aborted                 /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/asan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70
FAIL: SanitizerCommon-ubsan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (3910 of 5239)
******************** TEST 'SanitizerCommon-ubsan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=undefined  -m64 -fno-function-sections -funwind-tables -nobuiltininc -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/include -idirafter /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/lib/clang/22/include -resource-dir=/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build -Wl,-rpath,/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/lib/linux  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/ubsan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/compiler_rt_build/test/sanitizer_common/ubsan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2

mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
…158489)

Add support for `lifetimebound` attributes in the lifetime safety
analysis to track loans from function parameters to return values.

Implemented support for `lifetimebound` attributes on function
parameters

This change replaces the single `AssignOriginFact` with two separate
operations: `OriginFlowFact` and `KillOriginFact`. The key difference is
in semantics:

* Old `AssignOriginFact`: Replaced the destination origin's loans
entirely with the source origin's loans.
* New `OriginFlowFact`: Can now optionally merge the source origin's
loans to the destination's existing loans.
* New `KillOriginFact`: Clears all loans from an origin.

For function calls with `lifetimebound` parameters, we kill the the
return value' origin first then use `OriginFlowFact` to accumulate loans
from multiple parameters into the return value's origin - enabling
tracking multiple lifetimebound arguments.

- Added a new `LifetimeAnnotations.h/cpp` to provide helper functions
for inspecting and inferring lifetime annotations
- Moved several functions from `CheckExprLifetime.cpp` to the new file
to make them reusable

The `lifetimebound` attribute is a key mechanism for expressing lifetime
dependencies between function parameters and return values. This change
enables the lifetime safety analysis to properly track these
dependencies, allowing it to detect more potential dangling reference
issues.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:analysis clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer clang:temporal-safety Issue/FR relating to the lifetime analysis in Clang (-Wdangling, -Wreturn-local-addr) clang Clang issues not falling into any other category llvm:adt

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants