[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-17 Thread Mikael Holmen via cfe-commits


@@ -31,6 +31,19 @@
 namespace clang::lifetimes {
 namespace internal {
 
+static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,

mikaelholmen wrote:

Yep, thanks!

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-17 Thread Utkarsh Saxena via cfe-commits


@@ -31,6 +31,19 @@
 namespace clang::lifetimes {
 namespace internal {
 
+static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,

usx95 wrote:

Thanks for notifying. https://github.com/llvm/llvm-project/pull/172696 should 
address this.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-17 Thread Mikael Holmen via cfe-commits


@@ -31,6 +31,19 @@
 namespace clang::lifetimes {
 namespace internal {
 
+static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,

mikaelholmen wrote:

Hi @usx95 

If building without asserts this function is unused and we get
```
../../clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp:34:13: error: unused 
function 'DebugOnlyFunction' [-Werror,-Wunused-function]
   34 | static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,
  | ^
1 error generated.
``

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-17 Thread LLVM Continuous Integration via cfe-commits

llvm-ci wrote:

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

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


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)
...
[   OK ] AddressSanitizer.AtoiAndFriendsOOBTest (2267 ms)
[ RUN  ] AddressSanitizer.HasFeatureAddressSanitizerTest
[   OK ] AddressSanitizer.HasFeatureAddressSanitizerTest (0 ms)
[ RUN  ] AddressSanitizer.CallocReturnsZeroMem
[   OK ] AddressSanitizer.CallocReturnsZeroMem (16 ms)
[ DISABLED ] AddressSanitizer.DISABLED_TSDTest
[ RUN  ] AddressSanitizer.IgnoreTest
[   OK ] AddressSanitizer.IgnoreTest (0 ms)
[ RUN  ] AddressSanitizer.SignalTest
[   OK ] AddressSanitizer.SignalTest (196 ms)
[ RUN  ] AddressSanitizer.ReallocTest
[   OK ] AddressSanitizer.ReallocTest (34 ms)
[ RUN  ] AddressSanitizer.WrongFreeTest
[   OK ] AddressSanitizer.WrongFreeTest (139 ms)
[ RUN  ] AddressSanitizer.LongJmpTest
[   OK ] AddressSanitizer.LongJmpTest (0 ms)
[ RUN  ] AddressSanitizer.ThreadStackReuseTest
[   OK ] AddressSanitizer.ThreadStackReuseTest (10 ms)
[ DISABLED ] AddressSanitizer.DISABLED_MemIntrinsicUnalignedAccessTest
[ DISABLED ] AddressSanitizer.DISABLED_LargeFunctionSymbolizeTest
[ DISABLED ] AddressSanitizer.DISABLED_MallocFreeUnwindAndSymbolizeTest
[ RUN  ] AddressSanitizer.UseThenFreeThenUseTest
[   OK ] AddressSanitizer.UseThenFreeThenUseTest (117 ms)
[ RUN  ] AddressSanitizer.FileNameInGlobalReportTest
[   OK ] AddressSanitizer.FileNameInGlobalReportTest (122 ms)
[ DISABLED ] AddressSanitizer.DISABLED_StressStackReuseAndExceptionsTest
[ RUN  ] AddressSanitizer.MlockTest
[   OK ] AddressSanitizer.MlockTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadedTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowIn
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowLeft
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowRight
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOM
[ DISABLED ] AddressSanitizer.DISABLED_DemoDoubleFreeTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoNullDerefTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoFunctionStaticTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoTooMuchMemoryTest
[ RUN  ] AddressSanitizer.LongDoubleNegativeTest
[   OK ] AddressSanitizer.LongDoubleNegativeTest (0 ms)
[--] 19 tests from AddressSanitizer (28183 ms total)

[--] Global test environment tear-down
[==] 22 tests from 2 test suites ran. (28197 ms total)
[  PASSED  ] 22 tests.

  YOU HAVE 1 DISABLED TEST

Step 34 (run instrumented asan tests 
[aarch64/bluejay-userdebug/TQ3A.230805.001]) failure: run instrumented asan 
tests [aarch64/bluejay-userdebug/TQ3A.230805.001] (failure)
...
[ RUN  ] AddressSanitizer.HasFeatureAddressSanitizerTest
[   OK ] AddressSanitizer.HasFeatureAddressSanitizerTest (0 ms)
[ RUN  ] AddressSanitizer.CallocReturnsZeroMem
[   OK ] AddressSanitizer.CallocReturnsZeroMem (16 ms)
[ DISABLED ] AddressSanitizer.DISABLED_TSDTest
[ RUN  ] AddressSanitizer.IgnoreTest
[   OK ] AddressSanitizer.IgnoreTest (0 ms)
[ RUN  ] AddressSanitizer.SignalTest
[   OK ] AddressSanitizer.SignalTest (196 ms)
[ RUN  ] AddressSanitizer.ReallocTest
[   OK ] AddressSanitizer.ReallocTest (34 ms)
[ RUN  ] AddressSanitizer.WrongFreeTest
[   OK ] AddressSanitizer.WrongFreeTest (139 ms)
[ RUN  ] AddressSanitizer.LongJmpTest
[   OK ] AddressSanitizer.LongJmpTest (0 ms)
[ RUN  ] AddressSanitizer.ThreadStackReuseTest
[   OK ] AddressSanitizer.ThreadStackReuseTest (10 ms)
[ DISABLED ] AddressSanitizer.DISABLED_MemIntrinsicUnalignedAccessTest
[ DISABLED ] AddressSanitizer.DISABLED_LargeFunctionSymbolizeTest
[ DISABLED ] AddressSanitizer.DISABLED_MallocFreeUnwindAndSymbolizeTest
[ RUN  ] AddressSanitizer.UseThenFreeThenUseTest
[   OK ] AddressSanitizer.UseThenFreeThenUseTest (117 ms)
[ RUN  ] AddressSanitizer.FileNameInGlobalReportTest
[   OK ] AddressSanitizer.FileNameInGlobalReportTest (122 ms)
[ DISABLED ] AddressSanitizer.DISABLED_StressStackReuseAndExceptionsTest
[ RUN  ] AddressSanitizer.MlockTest
[   OK ] AddressSanitizer.MlockTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadedTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowIn
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowLeft
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowRight
[ DISABLED ]

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 closed https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 auto_merge_enabled 
https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits


@@ -186,13 +286,32 @@ void FactsGenerator::VisitConditionalOperator(const 
ConditionalOperator *CO) {
 void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
   // Assignment operators have special "kill-then-propagate" semantics

usx95 wrote:

We are doing this now behind `hasOrigins`. Shifted the comment inside the `if`

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits


@@ -275,22 +416,41 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
 // For explicit arguments, find the corresponding parameter
 // declaration.
 PVD = Method->getParamDecl(I - 1);
-} else if (I < FD->getNumParams())
+} else if (I < FD->getNumParams()) {
   // For free functions or static methods.
   PVD = FD->getParamDecl(I);
+}
 return PVD ? PVD->hasAttr() : false;
   };
   if (Args.empty())
 return;
-  bool killedSrc = false;
-  for (unsigned I = 0; I < Args.size(); ++I)
-if (IsGslConstruction || IsArgLifetimeBound(I)) {
-  if (!killedSrc) {
-killedSrc = true;
-killAndFlowOrigin(*Call, *Args[I]);
-  } else
-flowOrigin(*Call, *Args[I]);
+  bool KillSrc = true;
+  for (unsigned I = 0; I < Args.size(); ++I) {
+OriginList *ArgList = getOriginsList(*Args[I]);
+if (!ArgList)
+  continue;
+if (IsGslConstruction) {
+  // TODO: document with code example.
+  // std::string_view(const std::string_view& from)
+  if (isGslPointerType(Args[I]->getType())) {
+assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
+ArgList = getRValueOrigins(Args[I], ArgList);
+  }
+  if (isGslOwnerType(Args[I]->getType())) {
+// GSL construction creates a view that borrows from arguments.
+// This implies flowing origins through the list structure.
+flow(CallList, ArgList, KillSrc);
+KillSrc = false;
+  }
+} else if (IsArgLifetimeBound(I)) {
+  // Lifetimebound on a non-GSL-ctor function means the returned

usx95 wrote:

Interesting idea. Started a thread to discuss this further. Postponing for a 
future PR to try this out.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits


@@ -358,13 +513,13 @@ llvm::SmallVector 
FactsGenerator::issuePlaceholderLoans() {
 
   llvm::SmallVector PlaceholderLoanFacts;
   for (const ParmVarDecl *PVD : FD->parameters()) {
-if (hasOrigin(PVD)) {
-  const PlaceholderLoan *L =
-  FactMgr.getLoanMgr().createLoan(PVD);
-  OriginID OID = FactMgr.getOriginMgr().getOrCreate(*PVD);
-  PlaceholderLoanFacts.push_back(
-  FactMgr.createFact(L->getID(), OID));
-}
+OriginList *List = getOriginsList(*PVD);
+if (!List)
+  continue;
+const PlaceholderLoan *L =
+FactMgr.getLoanMgr().createLoan(PVD);
+PlaceholderLoanFacts.push_back(

usx95 wrote:

This directly effects annotation suggestions. We could do that but we would 
need to silence the annotation suggestions for inner origins because 
lifetimebound cannot express those.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits


@@ -180,8 +260,29 @@ void FactsGenerator::VisitConditionalOperator(const 
ConditionalOperator *CO) {
 void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
   // 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));
+  if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2) {
+
+const Expr *LHSExpr = OCE->getArg(0);
+const Expr *RHSExpr = OCE->getArg(1);
+
+if (const auto *DRE_LHS =

usx95 wrote:

Done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-16 Thread Utkarsh Saxena via cfe-commits


@@ -314,39 +474,34 @@ bool FactsGenerator::handleTestPoint(const 
CXXFunctionalCastExpr *FCE) {
   return false;
 }
 
-void FactsGenerator::handleAssignment(const Expr *LHSExpr,
-  const Expr *RHSExpr) {
-  if (!hasOrigin(LHSExpr))
-return;
-  // Find the underlying variable declaration for the left-hand side.
-  if (const auto *DRE_LHS =
-  dyn_cast(LHSExpr->IgnoreParenImpCasts())) {
-markUseAsWrite(DRE_LHS);
-if (const auto *VD_LHS = dyn_cast(DRE_LHS->getDecl())) {
-  // Kill the old loans of the destination origin and flow the new loans
-  // from the source origin.
-  killAndFlowOrigin(*VD_LHS, *RHSExpr);
-}
-  }
-}
-
 // A DeclRefExpr will be treated as a use of the referenced decl. It will be
 // checked for use-after-free unless it is later marked as being written to
 // (e.g. on the left-hand side of an assignment).
 void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
-  if (isPointerType(DRE->getType())) {
-UseFact *UF = FactMgr.createFact(DRE, FactMgr.getOriginMgr());
-CurrentBlockFacts.push_back(UF);
-assert(!UseFacts.contains(DRE));
-UseFacts[DRE] = UF;
+  OriginList *List = getOriginsList(*DRE);
+  if (!List)
+return;
+  // Remove the outer layer of origin which borrows from the decl directly. 
This
+  // is a use of the underlying decl.
+  List = getRValueOrigins(DRE, List);
+  // Skip if there is no inner origin (e.g., when it is not a pointer type).
+  if (!List)
+return;
+  llvm::SmallVector UsedOrigins;
+  OriginList *L = List;
+  while (L) {
+UsedOrigins.push_back(L->getOuterOriginID());
+L = L->peelOuterOrigin();
   }
+  UseFact *UF = FactMgr.createFact(DRE, std::move(UsedOrigins));

usx95 wrote:

Done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Yitzhak Mandelbaum via cfe-commits


@@ -186,13 +286,32 @@ void FactsGenerator::VisitConditionalOperator(const 
ConditionalOperator *CO) {
 void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
   // Assignment operators have special "kill-then-propagate" semantics

ymand wrote:

We should document somewhere that we assume assignment semantics of all 
user-defined assignment operators.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Yitzhak Mandelbaum via cfe-commits


@@ -133,24 +191,49 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
 const CXXNullPtrLiteralExpr *N) {
   /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
   /// pointers can use the same type of loan.
-  FactMgr.getOriginMgr().getOrCreate(*N);
+  getOriginsList(*N);
 }
 
 void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  if (!hasOrigin(ICE))
+  OriginList *Dest = getOriginsList(*ICE);
+  if (!Dest)
+return;
+  OriginList *SrcList = getOriginsList(*ICE->getSubExpr());
+
+  switch (ICE->getCastKind()) {
+  case CK_LValueToRValue:
+// TODO: Decide what to do for x-values here.
+if (!ICE->getSubExpr()->isLValue())
+  return;
+
+assert(SrcList && "LValue being cast to RValue has no origin list");
+// The result of an LValue-to-RValue cast on a reference-to-pointer like

ymand wrote:

nit: missing word after "like"?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand approved this pull request.


https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Yitzhak Mandelbaum via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.

ymand wrote:

Great. We can discuss further, but I suspect you won't get anything additional 
from a tree -- I don't believe that's come up in Nullability.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Gábor Horváth via cfe-commits


@@ -314,39 +474,34 @@ bool FactsGenerator::handleTestPoint(const 
CXXFunctionalCastExpr *FCE) {
   return false;
 }
 
-void FactsGenerator::handleAssignment(const Expr *LHSExpr,
-  const Expr *RHSExpr) {
-  if (!hasOrigin(LHSExpr))
-return;
-  // Find the underlying variable declaration for the left-hand side.
-  if (const auto *DRE_LHS =
-  dyn_cast(LHSExpr->IgnoreParenImpCasts())) {
-markUseAsWrite(DRE_LHS);
-if (const auto *VD_LHS = dyn_cast(DRE_LHS->getDecl())) {
-  // Kill the old loans of the destination origin and flow the new loans
-  // from the source origin.
-  killAndFlowOrigin(*VD_LHS, *RHSExpr);
-}
-  }
-}
-
 // A DeclRefExpr will be treated as a use of the referenced decl. It will be
 // checked for use-after-free unless it is later marked as being written to
 // (e.g. on the left-hand side of an assignment).
 void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
-  if (isPointerType(DRE->getType())) {
-UseFact *UF = FactMgr.createFact(DRE, FactMgr.getOriginMgr());
-CurrentBlockFacts.push_back(UF);
-assert(!UseFacts.contains(DRE));
-UseFacts[DRE] = UF;
+  OriginList *List = getOriginsList(*DRE);
+  if (!List)
+return;
+  // Remove the outer layer of origin which borrows from the decl directly. 
This
+  // is a use of the underlying decl.
+  List = getRValueOrigins(DRE, List);
+  // Skip if there is no inner origin (e.g., when it is not a pointer type).
+  if (!List)
+return;
+  llvm::SmallVector UsedOrigins;
+  OriginList *L = List;
+  while (L) {
+UsedOrigins.push_back(L->getOuterOriginID());
+L = L->peelOuterOrigin();
   }
+  UseFact *UF = FactMgr.createFact(DRE, std::move(UsedOrigins));

Xazax-hun wrote:

I wonder if the code would be simpler if we always had the same format 
everywhere for origin lists. E.g., if `UseFact` took an `OriginList` instead of 
a `SmallVector`. Might make it easier to switch to a tree in the future if we 
need it. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Gábor Horváth via cfe-commits


@@ -358,13 +513,13 @@ llvm::SmallVector 
FactsGenerator::issuePlaceholderLoans() {
 
   llvm::SmallVector PlaceholderLoanFacts;
   for (const ParmVarDecl *PVD : FD->parameters()) {
-if (hasOrigin(PVD)) {
-  const PlaceholderLoan *L =
-  FactMgr.getLoanMgr().createLoan(PVD);
-  OriginID OID = FactMgr.getOriginMgr().getOrCreate(*PVD);
-  PlaceholderLoanFacts.push_back(
-  FactMgr.createFact(L->getID(), OID));
-}
+OriginList *List = getOriginsList(*PVD);
+if (!List)
+  continue;
+const PlaceholderLoan *L =
+FactMgr.getLoanMgr().createLoan(PVD);
+PlaceholderLoanFacts.push_back(

Xazax-hun wrote:

What is the main reason to not create placeholder loans for the nested origins?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun approved this pull request.

I have some questions inline but overall looks good to me. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Gábor Horváth via cfe-commits


@@ -275,22 +416,41 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
 // For explicit arguments, find the corresponding parameter
 // declaration.
 PVD = Method->getParamDecl(I - 1);
-} else if (I < FD->getNumParams())
+} else if (I < FD->getNumParams()) {
   // For free functions or static methods.
   PVD = FD->getParamDecl(I);
+}
 return PVD ? PVD->hasAttr() : false;
   };
   if (Args.empty())
 return;
-  bool killedSrc = false;
-  for (unsigned I = 0; I < Args.size(); ++I)
-if (IsGslConstruction || IsArgLifetimeBound(I)) {
-  if (!killedSrc) {
-killedSrc = true;
-killAndFlowOrigin(*Call, *Args[I]);
-  } else
-flowOrigin(*Call, *Args[I]);
+  bool KillSrc = true;
+  for (unsigned I = 0; I < Args.size(); ++I) {
+OriginList *ArgList = getOriginsList(*Args[I]);
+if (!ArgList)
+  continue;
+if (IsGslConstruction) {
+  // TODO: document with code example.
+  // std::string_view(const std::string_view& from)
+  if (isGslPointerType(Args[I]->getType())) {
+assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
+ArgList = getRValueOrigins(Args[I], ArgList);
+  }
+  if (isGslOwnerType(Args[I]->getType())) {
+// GSL construction creates a view that borrows from arguments.
+// This implies flowing origins through the list structure.
+flow(CallList, ArgList, KillSrc);
+KillSrc = false;
+  }
+} else if (IsArgLifetimeBound(I)) {
+  // Lifetimebound on a non-GSL-ctor function means the returned

Xazax-hun wrote:

Probably not for this PR, but I was wondering if we can sometimes say something 
about the nested origins as well. In Rust, there is this implicit constraint 
that if I have `int * `A * `B`, the outer pointer cannot outlive the inner 
pointer. So, there is an outlives relationship between `A` and `B`. 

So in case `int * `A * `B` has to outlive `int * `C * `D`, so `B` has to 
outlive `D`, I wonder if we can also deduce that `A` also has to outlive `D` 
(since `A` has to outlive `B`). And I wonder if this is helpful in catching any 
bugs. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-15 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun edited 
https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -539,11 +578,28 @@ int* return_pointer_to_parameter(int a) {
 // expected-note@-1 {{returned here}}
 }
 
-const int& return_reference_to_parameter(int a)
-{
-const int &b = a; 
-return b; // expected-warning {{address of stack memory is 
returned later}}
-  // expected-note@-1 {{returned here}}
+const int& return_reference_to_parameter(int a) {
+const int &b = a;   // expected-warning {{address of stack memory is 
returned later}}
+return b;   // expected-note {{returned here}}
+}
+int return_reference_to_parameter_no_error(int a) {
+const int &b = a;
+return b;
+}
+
+const int& reference_via_conditional(int a, int b, bool cond) {
+const int &c = (cond ? ((a)) : (b));  // expected-warning 2 {{address of 
stack memory is returned later}}
+return c; // expected-note 2 {{returned here}}
+}
+const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
+const int &c = cond ? a : b;  // expected-warning 2 {{address of stack 
memory is returned later}}
+const int* d = &c;
+return d; // expected-note 2 {{returned here}}
+}
+// FIXME: Dereference of a pointer does not track the reference.

usx95 wrote:

This is done in a followup PR.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -133,24 +191,47 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
 const CXXNullPtrLiteralExpr *N) {
   /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
   /// pointers can use the same type of loan.
-  FactMgr.getOriginMgr().getOrCreate(*N);
+  getOriginsList(*N);
 }
 
 void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  if (!hasOrigin(ICE))
+  OriginList *Dest = getOriginsList(*ICE);
+  if (!Dest)
+return;
+  OriginList *SrcList = getOriginsList(*ICE->getSubExpr());
+
+  if (ICE->getCastKind() == CK_LValueToRValue) {
+// TODO: Decide what to do for x-values here.
+if (!ICE->getSubExpr()->isLValue())
+  return;
+
+assert(SrcList && "LValue being cast to RValue has no origin list");
+// The result of an LValue-to-RValue cast on a reference-to-pointer like
+// has the inner origin. Get rid of the outer origin.
+flow(getOriginsList(*ICE), getRValueOrigins(ICE->getSubExpr(), SrcList),
+ /*Kill=*/true);
+return;
+  }
+  if (ICE->getCastKind() == CK_NullToPointer) {
+getOriginsList(*ICE);
+// TODO: Flow into them a null origin.
+return;
+  }
+  if (ICE->getCastKind() == CK_NoOp ||
+  ICE->getCastKind() == CK_ConstructorConversion ||
+  ICE->getCastKind() == CK_UserDefinedConversion)
+flow(Dest, SrcList, /*Kill=*/true);
+  if (ICE->getCastKind() == CK_FunctionToPointerDecay ||
+  ICE->getCastKind() == CK_BuiltinFnToFnPtr ||
+  ICE->getCastKind() == CK_ArrayToPointerDecay) {
+// Ignore function-to-pointer decays.
 return;
-  // An ImplicitCastExpr node itself gets an origin, which flows from the
-  // origin of its sub-expression (after stripping its own parens/casts).
-  killAndFlowOrigin(*ICE, *ICE->getSubExpr());
+  }

usx95 wrote:

Done

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -7,85 +7,152 @@
 
//===--===//
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 
 namespace clang::lifetimes::internal {
 
-void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
-  OS << OID << " (";
-  Origin O = getOrigin(OID);
-  if (const ValueDecl *VD = O.getDecl())
-OS << "Decl: " << VD->getNameAsString();
-  else if (const Expr *E = O.getExpr())
-OS << "Expr: " << E->getStmtClassName();
-  else
-OS << "Unknown";
-  OS << ")";
+bool hasOrigins(QualType QT) {
+  return QT->isPointerOrReferenceType() || isGslPointerType(QT);
 }
 
-Origin &OriginManager::addOrigin(OriginID ID, const clang::ValueDecl &D) {
-  AllOrigins.emplace_back(ID, &D);
-  return AllOrigins.back();
+/// Determines if an expression has origins that need to be tracked.
+///
+/// An expression has origins if:
+/// - It's a glvalue (has addressable storage), OR
+/// - Its type is pointer-like (pointer, reference, or gsl::Pointer)
+///
+/// Examples:
+/// - `int x; x` : has origin (glvalue)
+/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
+/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
+/// - `42` : no origin (prvalue of non-pointer type)
+/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
+bool hasOrigins(const Expr *E) {
+  return E->isGLValue() || hasOrigins(E->getType());
 }
 
-Origin &OriginManager::addOrigin(OriginID ID, const clang::Expr &E) {
-  AllOrigins.emplace_back(ID, &E);
-  return AllOrigins.back();
+/// Returns true if the declaration has its own storage that can be borrowed.
+///
+/// References generally have no storage - they are aliases to other storage.
+/// For example:
+///   int x;  // has storage (can issue loans to x's storage)
+///   int& r = x; // no storage (r is an alias to x's storage)
+///   int* p; // has storage (the pointer variable p itself has storage)
+///
+/// TODO: Handle lifetime extension. References initialized by temporaries
+/// can have storage when the temporary's lifetime is extended:
+///   const int& r = 42; // temporary has storage, lifetime extended
+///   Foo&& f = Foo{};   // temporary has storage, lifetime extended
+/// Currently, this function returns false for all reference types.
+bool doesDeclHaveStorage(const ValueDecl *D) {
+  return !D->getType()->isReferenceType();
 }
 
-// TODO: Mark this method as const once we remove the call to getOrCreate.
-OriginID OriginManager::get(const Expr &E) {
-  if (auto *ParenIgnored = E.IgnoreParens(); ParenIgnored != &E)
-return get(*ParenIgnored);
-  auto It = ExprToOriginID.find(&E);
-  if (It != ExprToOriginID.end())
-return It->second;
-  // If the expression itself has no specific origin, and it's a reference
-  // to a declaration, its origin is that of the declaration it refers to.
-  // For pointer types, where we don't pre-emptively create an origin for the
-  // DeclRefExpr itself.
-  if (const auto *DRE = dyn_cast(&E))
-return get(*DRE->getDecl());
-  // TODO: This should be an assert(It != ExprToOriginID.end()). The current
-  // implementation falls back to getOrCreate to avoid crashing on
-  // yet-unhandled pointer expressions, creating an empty origin for them.
-  return getOrCreate(E);
+OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) {
+  OriginID NewID = getNextOriginID();
+  AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
+  return new (ListAllocator.Allocate()) OriginList(NewID);
 }
 
-OriginID OriginManager::get(const ValueDecl &D) {
-  auto It = DeclToOriginID.find(&D);
-  // TODO: This should be an assert(It != DeclToOriginID.end()). The current
-  // implementation falls back to getOrCreate to avoid crashing on
-  // yet-unhandled pointer expressions, creating an empty origin for them.
-  if (It == DeclToOriginID.end())
-return getOrCreate(D);
+OriginList *OriginManager::createNode(const Expr *E, QualType QT) {
+  OriginID NewID = getNextOriginID();
+  AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
+  return new (ListAllocator.Allocate()) OriginList(NewID);
+}
 
-  return It->second;
+template 
+OriginList *OriginManager::buildListForType(QualType QT, const T *Node) {
+  assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
+  OriginList *Root = createNode(Node, QT);
+  if (QT->isPointerOrReferenceType()) {
+QualType PointeeTy = QT->getPointeeType();
+// We recurse if the pointee type is pointer-like, to build the next
+// level in the origin tree. E.g., for T*& / View&.
+if (hasOrigins(PointeeTy))
+  Root->setInnerOriginList(buildListForType(PointeeTy, Node));
---

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.

usx95 wrote:

Aah I see what you were referring to.

Yes, my idea is to move from an `OriginList` to `OriginTree` to support 
multiple inner pointers like `std::pair` . This could be 
represented as a pre-order array but my initial preference is to have a Tree 
representation for better understanding.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -186,8 +284,28 @@ void FactsGenerator::VisitConditionalOperator(const 
ConditionalOperator *CO) {
 void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
   // 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));
+  if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2) {
+
+const Expr *LHSExpr = OCE->getArg(0);
+const Expr *RHSExpr = OCE->getArg(1);
+
+if (const auto *DRE_LHS =
+dyn_cast(LHSExpr->IgnoreParenImpCasts())) {
+  OriginList *LHSList = getOriginsList(*DRE_LHS);

usx95 wrote:

Good point. I missed this. Added a precheck of hasOrigins.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -943,3 +1065,235 @@ void parentheses(bool cond) {
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 }
+
+namespace GH162834 {

usx95 wrote:

Yes. This is https://github.com/llvm/llvm-project/issues/162834


https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits


@@ -552,6 +608,71 @@ const int& get_ref_to_local() {
   // expected-note@-1 {{returned here}}
 }
 
+void test_view_pointer() {
+  View* vp;
+  {
+View v;
+vp = &v; // expected-warning {{object whose reference is captured does 
not live long enough}}
+  }  // expected-note {{destroyed here}}
+  vp->use(); // expected-note {{later used here}}
+}
+
+void test_view_double_pointer() {
+  View** vpp;
+  {
+View* vp = nullptr;
+vpp = &vp;   // expected-warning {{object whose reference is captured does 
not live long enough}}
+  }  // expected-note {{destroyed here}}
+  (**vpp).use(); // expected-note {{later used here}}
+}
+
+struct PtrHolder {
+  int* ptr;
+  int* const& getRef() const [[clang::lifetimebound]] { return ptr; }
+};
+
+int* const& test_ref_to_ptr() {
+  PtrHolder a;
+  int *const &ref = a.getRef();  // expected-warning {{address of stack memory 
is returned later}}
+  return ref;  // expected-note {{returned here}}
+}
+int* const test_ref_to_ptr_no_error() {
+  PtrHolder a;
+  int *const &ref = a.getRef();
+  return ref;
+}
+
+int** return_inner_ptr_addr(int*** ppp [[clang::lifetimebound]]);
+void test_lifetimebound_multi_level() {
+  int** result;
+  {
+int* p = nullptr;
+int** pp = &p;  
+int*** ppp = &pp; // expected-warning {{object whose reference is captured 
does not live long enough}}
+result = return_inner_ptr_addr(ppp);
+  }   // expected-note {{destroyed here}}
+  (void)**result; // expected-note {{used here}}
+}
+
+// FIXME: Assignment does not track the dereference of a pointer.
+void test_assign_through_double_ptr() {
+  int a = 1, b = 2;
+  int* p = &a;
+  int** pp = &p;
+  {
+int c = 3;
+*pp = &c;
+  }
+  (void)**pp;
+}
+
+int** test_ternary_double_ptr(bool cond) {

usx95 wrote:

None of the above works at this point.

The plan is to handle this using **flow-sensitive alias tracking** (similar to 
Polonius in Rust). This should be handled once we start creating aliases among 
origins due to cycles in subtype-relations.

### Alias Creation via Multilevel Origins

```cpp
int* pa = &a;
// Opa <- loan to 'a'

int** result = &pa;
// [Or1, Or2] <- [Opa_outer, Opa]
// Or1 <- loan to 'pa' (the storage of pa)
// Or2 <- Opa (what pa points to) 
```

The missing piece is creating a **bidirectional alias relationship**: `Or2` and 
`Opa` should be treated as aliases/subtype of each other, forming an SCC in the 
origin-flow graph. This should for all inner origins at all levels. For 
example, here we should have `// Opa <- Or2`flow fact but not for `Or1` and 
`Opa_outer`.

(Thinking about the case 3 to not make `Opa` and `Opb` alias of each other. I 
think it involves something on the lines of dropping flow facts which involve 
dead origins)

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-11 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344




  
Unicorn! · GitHub

  body {
background-color: #f1f1f1;
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  }

  .container { margin: 50px auto 40px auto; width: 600px; text-align: 
center; }

  a { color: #4183c4; text-decoration: none; }
  a:hover { text-decoration: underline; }

  h1 { letter-spacing: -1px; line-height: 60px; font-size: 60px; 
font-weight: 100; margin: 0px; text-shadow: 0 1px 0 #fff; }
  p { color: rgba(0, 0, 0, 0.5); margin: 10px 0 10px; font-size: 18px; 
font-weight: 200; line-height: 1.6em;}

  ul { list-style: none; margin: 25px 0; padding: 0; }
  li { display: table-cell; font-weight: bold; width: 1%; }

  .logo { display: inline-block; margin-top: 35px; }
  .logo-img-2x { display: none; }
  @media
  only screen and (-webkit-min-device-pixel-ratio: 2),
  only screen and (   min--moz-device-pixel-ratio: 2),
  only screen and ( -o-min-device-pixel-ratio: 2/1),
  only screen and (min-device-pixel-ratio: 2),
  only screen and (min-resolution: 192dpi),
  only screen and (min-resolution: 2dppx) {
.logo-img-1x { display: none; }
.logo-img-2x { display: inline-block; }
  }

  #suggestions {
margin-top: 35px;
color: #ccc;
  }
  #suggestions a {
color: #66;
font-weight: 200;
font-size: 14px;
margin: 0 10px;
  }


  
  


  

  

  No server is currently available to service your 
request.
  Sorry about that. Please try refreshing and contact us if the problem 
persists.
  
https://github.com/contact";>Contact Support —
https://www.githubstatus.com";>GitHub Status —
https://twitter.com/githubstatus";>@githubstatus
  

  

  

  

  

  


___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-09 Thread Yitzhak Mandelbaum via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.

ymand wrote:

In that case, I would recommend you consider whether this new design could 
effectively support template cases. That is, you don't have support them with 
this change, but you should be relatively sure it will work for them in the 
future. My intuition is that a vector is still the right structure, because 
that's exactly what we use for Nullability. The vector represents the pre-order 
traversal of the type tree, with each element representing one pointer type.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)
+/// * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the list has size 2:
+/// * Root: origin for the pointer variable `p`
+/// * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+/// * Root: origin for the view object itself
+/// * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the list has size 3:
+/// * Root: origin for `pp` itself
+/// * Pointee: origin for `*pp` (what `pp` points to)
+/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The list structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+class OriginList {
 public:
-  OriginManager() = default;
+  OriginList(OriginID OID) : OuterOID(OID) {}
+
+  OriginList *peelOuterOrigin() { return InnerList; }
+  OriginID getOuterOriginID() const { return OuterOID; }
 
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
+  void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
 
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
+  size_t getLength() const {
+size_t Length = 1;
+const OriginList *T = this;
+while (T->InnerList) {
+  T = T->InnerList;
+  Length++;
+}
+return Length;
+  }
 
-  OriginID get(const ValueDecl &D);
+private:
+  OriginID OuterOID;
+  OriginList *InnerList = nullptr;
+};
 
-  OriginID getOrCreate(const Expr &E);
+bool hasOrigins(QualType QT);
+bool hasOrigins(const Expr *E);
+bool doesDeclHaveStorage(const ValueDecl *D);
+
+/// Manages the creation, storage, and retrieval of origins for pointer-like
+/// variables and expressions.
+class OriginManager {
+public:
+  explicit OriginManager(ASTContext &AST) : AST(AST) {}
+
+  /// Gets or creates the OriginList for a given ValueDecl.
+  ///
+  /// Creates a list structure mirroring the levels of indirection in the
+  /// declaration's type (e.g., `int** p` creates list of size 2).
+  ///
+  /// \returns The OriginList, or nullptr if the type is not pointer-like.
+  OriginList *getOrCreateList(const ValueDecl *D);
+
+  /// Gets or creates the OriginList for a given Expr.
+  ///
+  /// Creates a list based on the expression's type and value category:
+  /// - Lvalues get an implicit reference level (modeling addressability)
+  /// - Rvalues of non-pointer type return nullptr (no trackable origin)
+  /// - DeclRefExpr may reuse the underlying declaration's list
+  ///
+  /// \returns The OriginList, or nullptr for non-pointer rvalues.
+  OriginList *getOrCreateList(const Expr *E, size_t Depth = 0);
 
   const Origin &getOrigin(OriginID ID) const;
 
   llvm::ArrayRef getOrigins() const { return AllOrigins; }
 
-  OriginID getOrCreate(const ValueDecl &D);
-
   unsigned getNumOrigins() const { return NextOriginID.Value; }
 
   void dump(OriginID OID, llvm::raw_ostream &OS) const;
 
 private:
   OriginID getNextOriginID() { return NextOriginID++; }
 
+  OriginList *createNode(const ValueDecl *D, QualType QT) {
+OriginID NewID = getNextOriginID();
+AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
+return new (ListAllocator.Allocate()) OriginList(NewID);
+  }
+
+  OriginList *createNode(const Expr *E, QualType QT) {
+OriginID NewID = getNextOriginID();
+AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
+return new (ListAllocator.Allocate()) OriginList(NewID);
+  }

usx95 wrote:

Moved definitions to .cpp file

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -155,21 +221,35 @@ void FactsGenerator::VisitUnaryOperator(const 
UnaryOperator *UO) {
 
 void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
   if (const Expr *RetExpr = RS->getRetValue()) {
-if (hasOrigin(RetExpr)) {
-  OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
-  EscapesInCurrentBlock.push_back(
-  FactMgr.createFact(OID, RetExpr));
-}
+if (OriginList *List = getOriginsList(*RetExpr))
+  for (OriginList *L = List; L; L = L->peelOuterOrigin())
+EscapesInCurrentBlock.push_back(FactMgr.createFact(
+L->getOuterOriginID(), RetExpr));
   }
 }
 
 void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {

usx95 wrote:

Makes sense. Added a TODO.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)
+/// * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the list has size 2:
+/// * Root: origin for the pointer variable `p`
+/// * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+/// * Root: origin for the view object itself
+/// * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the list has size 3:
+/// * Root: origin for `pp` itself
+/// * Pointee: origin for `*pp` (what `pp` points to)
+/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The list structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+class OriginList {
 public:
-  OriginManager() = default;
+  OriginList(OriginID OID) : OuterOID(OID) {}
+
+  OriginList *peelOuterOrigin() { return InnerList; }
+  OriginID getOuterOriginID() const { return OuterOID; }
 
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
+  void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
 
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
+  size_t getLength() const {

usx95 wrote:

This is used only in assertions and therefore I didn't want to make this a 
member just yet.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -210,11 +311,21 @@ void FactsGenerator::VisitInitListExpr(const InitListExpr 
*ILE) {
 
 void FactsGenerator::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *MTE) {
-  if (!hasOrigin(MTE))
+  OriginList *MTEList = getOriginsList(*MTE);
+  OriginList *SubExprList = getOriginsList(*MTE->getSubExpr());
+  if (!MTEList)

usx95 wrote:

No. Good point. Moved it to after the return.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -155,18 +155,19 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.

usx95 wrote:

Done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)
+/// * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the list has size 2:
+/// * Root: origin for the pointer variable `p`
+/// * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+/// * Root: origin for the view object itself
+/// * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the list has size 3:
+/// * Root: origin for `pp` itself
+/// * Pointee: origin for `*pp` (what `pp` points to)
+/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The list structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+class OriginList {
 public:
-  OriginManager() = default;
+  OriginList(OriginID OID) : OuterOID(OID) {}
+
+  OriginList *peelOuterOrigin() { return InnerList; }
+  OriginID getOuterOriginID() const { return OuterOID; }
 
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
+  void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
 
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
+  size_t getLength() const {
+size_t Length = 1;
+const OriginList *T = this;
+while (T->InnerList) {
+  T = T->InnerList;
+  Length++;
+}
+return Length;
+  }
 
-  OriginID get(const ValueDecl &D);
+private:
+  OriginID OuterOID;
+  OriginList *InnerList = nullptr;
+};
 
-  OriginID getOrCreate(const Expr &E);
+bool hasOrigins(QualType QT);
+bool hasOrigins(const Expr *E);
+bool doesDeclHaveStorage(const ValueDecl *D);
+
+/// Manages the creation, storage, and retrieval of origins for pointer-like
+/// variables and expressions.
+class OriginManager {
+public:
+  explicit OriginManager(ASTContext &AST) : AST(AST) {}
+
+  /// Gets or creates the OriginList for a given ValueDecl.
+  ///
+  /// Creates a list structure mirroring the levels of indirection in the
+  /// declaration's type (e.g., `int** p` creates list of size 2).
+  ///
+  /// \returns The OriginList, or nullptr if the type is not pointer-like.
+  OriginList *getOrCreateList(const ValueDecl *D);
+
+  /// Gets or creates the OriginList for a given Expr.
+  ///
+  /// Creates a list based on the expression's type and value category:
+  /// - Lvalues get an implicit reference level (modeling addressability)
+  /// - Rvalues of non-pointer type return nullptr (no trackable origin)
+  /// - DeclRefExpr may reuse the underlying declaration's list
+  ///
+  /// \returns The OriginList, or nullptr for non-pointer rvalues.
+  OriginList *getOrCreateList(const Expr *E, size_t Depth = 0);

usx95 wrote:

Thanks for spotting. Looks completely like a leftover which is unused.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -155,21 +221,35 @@ void FactsGenerator::VisitUnaryOperator(const 
UnaryOperator *UO) {
 
 void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
   if (const Expr *RetExpr = RS->getRetValue()) {
-if (hasOrigin(RetExpr)) {
-  OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
-  EscapesInCurrentBlock.push_back(
-  FactMgr.createFact(OID, RetExpr));
-}
+if (OriginList *List = getOriginsList(*RetExpr))
+  for (OriginList *L = List; L; L = L->peelOuterOrigin())
+EscapesInCurrentBlock.push_back(FactMgr.createFact(
+L->getOuterOriginID(), RetExpr));
   }
 }
 
 void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
-  if (BO->isAssignmentOp())
-handleAssignment(BO->getLHS(), BO->getRHS());
+  if (BO->isCompoundAssignmentOp())
+return;
+  if (BO->isAssignmentOp()) {
+const Expr *LHSExpr = BO->getLHS();
+const Expr *RHSExpr = BO->getRHS();
+
+if (const auto *DRE_LHS =

usx95 wrote:

Yes. Added a TODO here as this is the place where it needs to be done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.

usx95 wrote:

Not immediately but I had some plans for such special cases in the future. The 
pre-existing analysis calls this Container of pointers using a heuristic where 
an `Owner` class is instantiated with a pointer type.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -155,18 +155,19 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
 
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)

usx95 wrote:

Done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -237,13 +348,25 @@ void FactsGenerator::handleGSLPointerConstruction(const 
CXXConstructExpr *CCE) {
   assert(isGslPointerType(CCE->getType()));
   if (CCE->getNumArgs() != 1)
 return;
-  if (hasOrigin(CCE->getArg(0)))
-killAndFlowOrigin(*CCE, *CCE->getArg(0));
-  else
+
+  const Expr *Arg = CCE->getArg(0);
+  if (isGslPointerType(Arg->getType())) {
+OriginList *ArgList = getOriginsList(*Arg);
+assert(ArgList && "GSL pointer argument should have an origin list");
+// GSL pointer is constructed from another gsl pointer.
+// Example:
+//  View(View v);
+//  View(const View &v);
+if (Arg->isGLValue())

usx95 wrote:

Hmm. This indeed looks like a pattern of forcing an `LValueToRValue` conversion.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)

usx95 wrote:

Changed to `Outer` and `Inner` to match the updated field names.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits


@@ -64,29 +93,48 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginTree *VDTree = getTree(*VD);
+if (!VDTree)
+  continue;
+OriginTree *InitTree = getTree(*InitExpr);
+assert(InitTree && "VarDecl had origins but InitExpr did not");
+// Special handling for rvalue references initialized with xvalues.
+// For declarations like `Ranges&& r = std::move(ranges);`, the rvalue
+// reference should directly refer to the object being moved from,
+// rather than creating a new indirection level. We skip the outer
+// reference level and flow the pointee origins directly.
+if (VD->getType()->isRValueReferenceType() && InitExpr->isXValue()) {
+  flow(VDTree->Pointee, InitTree->Pointee, /*Kill=*/true);
+  continue;
+}
+flow(VDTree, InitTree, /*Kill=*/true);
+  }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references and PR values.
+  if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())

usx95 wrote:

Nopes. https://godbolt.org/z/ccxjcdnz4 Still lvalue. ¯\\\_(ツ)_/¯

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-08 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -155,18 +155,19 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
 
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)

ymand wrote:

Use of ArrayRef forces a copy. But, I would imagine at construction time its 
common that the caller has constructed the list specificaly for this fact (like 
line 472 of FactsGenerator.cpp below). At least, provide an overload that 
allows passing a constructed object by value (to passing by move)?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)
+/// * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the list has size 2:
+/// * Root: origin for the pointer variable `p`
+/// * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+/// * Root: origin for the view object itself
+/// * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the list has size 3:
+/// * Root: origin for `pp` itself
+/// * Pointee: origin for `*pp` (what `pp` points to)
+/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The list structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+class OriginList {
 public:
-  OriginManager() = default;
+  OriginList(OriginID OID) : OuterOID(OID) {}
+
+  OriginList *peelOuterOrigin() { return InnerList; }
+  OriginID getOuterOriginID() const { return OuterOID; }
 
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
+  void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
 
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
+  size_t getLength() const {
+size_t Length = 1;
+const OriginList *T = this;
+while (T->InnerList) {
+  T = T->InnerList;
+  Length++;
+}
+return Length;
+  }
 
-  OriginID get(const ValueDecl &D);
+private:
+  OriginID OuterOID;
+  OriginList *InnerList = nullptr;
+};
 
-  OriginID getOrCreate(const Expr &E);
+bool hasOrigins(QualType QT);
+bool hasOrigins(const Expr *E);
+bool doesDeclHaveStorage(const ValueDecl *D);
+
+/// Manages the creation, storage, and retrieval of origins for pointer-like
+/// variables and expressions.
+class OriginManager {
+public:
+  explicit OriginManager(ASTContext &AST) : AST(AST) {}
+
+  /// Gets or creates the OriginList for a given ValueDecl.
+  ///
+  /// Creates a list structure mirroring the levels of indirection in the
+  /// declaration's type (e.g., `int** p` creates list of size 2).
+  ///
+  /// \returns The OriginList, or nullptr if the type is not pointer-like.
+  OriginList *getOrCreateList(const ValueDecl *D);
+
+  /// Gets or creates the OriginList for a given Expr.
+  ///
+  /// Creates a list based on the expression's type and value category:
+  /// - Lvalues get an implicit reference level (modeling addressability)
+  /// - Rvalues of non-pointer type return nullptr (no trackable origin)
+  /// - DeclRefExpr may reuse the underlying declaration's list
+  ///
+  /// \returns The OriginList, or nullptr for non-pointer rvalues.
+  OriginList *getOrCreateList(const Expr *E, size_t Depth = 0);
 
   const Origin &getOrigin(OriginID ID) const;
 
   llvm::ArrayRef getOrigins() const { return AllOrigins; }
 
-  OriginID getOrCreate(const ValueDecl &D);
-
   unsigned getNumOrigins() const { return NextOriginID.Value; }
 
   void dump(OriginID OID, llvm::raw_ostream &OS) const;
 
 private:
   OriginID getNextOriginID() { return NextOriginID++; }
 
+  OriginList *createNode(const ValueDecl *D, QualType QT) {
+OriginID NewID = getNextOriginID();
+AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
+return new (ListAllocator.Allocate()) OriginList(NewID);
+  }
+
+  OriginList *createNode(const Expr *E, QualType QT) {
+OriginID NewID = getNextOriginID();
+AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
+return new (ListAllocator.Allocate()) OriginList(NewID);
+  }

ymand wrote:

why define these in the header (given that they are private)?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -64,29 +93,42 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginList *VDList = getOriginsList(*VD);
+if (!VDList)
+  continue;
+OriginList *InitList = getOriginsList(*InitExpr);
+assert(InitList && "VarDecl had origins but InitExpr did not");
+flow(VDList, InitList, /*Kill=*/true);
+  }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references as they are lifetimes are not interesting. Skip

ymand wrote:

```suggestion
  // Skip function references as their lifetimes are not interesting. Skip
```

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.

ymand wrote:

Do you track through templates, like `std::optional`?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -52,41 +62,118 @@ struct Origin {
   }
 };
 
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
+/// A list of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the list contains an OriginID representing a level of
+/// indirection. The list structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the list has size 2:
+/// * Root: origin for the reference storage itself (the lvalue `x`)
+/// * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the list has size 2:
+/// * Root: origin for the pointer variable `p`
+/// * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the list has size 2:
+/// * Root: origin for the view object itself
+/// * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the list has size 3:
+/// * Root: origin for `pp` itself
+/// * Pointee: origin for `*pp` (what `pp` points to)
+/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The list structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+class OriginList {
 public:
-  OriginManager() = default;
+  OriginList(OriginID OID) : OuterOID(OID) {}
+
+  OriginList *peelOuterOrigin() { return InnerList; }
+  OriginID getOuterOriginID() const { return OuterOID; }
 
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
+  void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
 
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
+  size_t getLength() const {

ymand wrote:

might this be worth tracking eagerly so it doesn't need to be (frequently?) 
recomputed?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -155,18 +155,19 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.

ymand wrote:

super nit: maybe explain the choice of 1 as the default length.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits


@@ -64,29 +93,42 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginList *VDList = getOriginsList(*VD);
+if (!VDList)
+  continue;
+OriginList *InitList = getOriginsList(*InitExpr);
+assert(InitList && "VarDecl had origins but InitExpr did not");
+flow(VDList, InitList, /*Kill=*/true);
+  }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references as they are lifetimes are not interesting. Skip
+  // EnumConstants as they are not GLValues.
+  if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() ||
+  DRE->getEnumConstantDecl())
+return;
+  assert(DRE->isGLValue());
   handleUse(DRE);
-  // For non-pointer/non-view types, a reference to the variable's storage
-  // is a borrow. We create a loan for it.
-  // For pointer/view types, we stick to the existing model for now and do
-  // not create an extra origin for the l-value expression itself.
-
-  // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
-  // not sufficient to model the different levels of indirection. The current
-  // single-origin model cannot distinguish between a loan to the variable's
-  // storage and a loan to what it points to. A multi-origin model would be
-  // required for this.
-  if (!isPointerType(DRE->getType())) {
-if (const Loan *L = createLoan(FactMgr, DRE)) {
-  OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
-  CurrentBlockFacts.push_back(
-  FactMgr.createFact(L->ID, ExprOID));
-}
+  // For all declarations with storage (non-references), we issue a loan
+  // representing the borrow of the variable's storage itself.
+  //
+  // Examples:
+  //   - `int x; x` issues loan to x's storage
+  //   - `int* p; p` issues loan to p's storage (the pointer variable)
+  //   - `View v; v` issues loan to v's storage (the view object)
+  //   - `int& r = x; r`issues no loan (r has no storage, it's an alias to x)

ymand wrote:

```suggestion
  //   - `int& r = x; r` issues no loan (r has no storage, it's an alias to x)
```

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand commented:

Partial review. Looks good so far!

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-04 Thread Yitzhak Mandelbaum via cfe-commits

ymand wrote:

> 4. **Multi-Level Flow Propagation**: The `flow` function propagates origins 
> through all depths of the lists with a critical assertion:
>`assert(Dst->getDepth() == Src->getDepth() && "Lists must have the same 
> length");` This ensures type safety in origin propagation during expression 
> handling. (Rant with relief: This `assert`​ was quite hard to get right but 
> it helped make the right changes).

rant acknowledged. :) Sounds like struggling to get an induction hypothesis 
correct :)

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -210,11 +317,24 @@ void FactsGenerator::VisitInitListExpr(const InitListExpr 
*ILE) {
 
 void FactsGenerator::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *MTE) {
-  if (!hasOrigin(MTE))
+  OriginTree *MTETree = getTree(*MTE);
+  OriginTree *SubExprTree = getTree(*MTE->getSubExpr());
+  if (!MTETree)
 return;
-  // A temporary object's origin is the same as the origin of the
-  // expression that initializes it.
-  killAndFlowOrigin(*MTE, *MTE->getSubExpr());
+  if (MTE->isGLValue()) {
+assert(!SubExprTree ||
+   MTETree->getDepth() == SubExprTree->getDepth() + 1 && "todo doc.");
+// Issue a loan to the MTE.
+// const Loan *L = createLoan(FactMgr, MTE);

usx95 wrote:

Done. Added a TODO instead.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -127,24 +177,46 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
 const CXXNullPtrLiteralExpr *N) {
   /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
   /// pointers can use the same type of loan.
-  FactMgr.getOriginMgr().getOrCreate(*N);
+  getTree(*N);
 }
 
 void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  if (!hasOrigin(ICE))
+  OriginTree *Dest = getTree(*ICE);
+  if (!Dest)
+return;
+  OriginTree *SrcTree = getTree(*ICE->getSubExpr());
+
+  if (ICE->getCastKind() == CK_LValueToRValue) {
+// TODO: Decide what to do for x-values here.
+if (!ICE->getSubExpr()->isLValue())
+  return;
+
+assert(SrcTree && "LValue being cast to RValue has no origin tree");
+// The result of an LValue-to-RValue cast on a reference-to-pointer like
+// has the inner origin. Get rid of the outer origin.
+flow(getTree(*ICE), SrcTree->Pointee, /*Kill=*/true);

usx95 wrote:

Added `getOuterOriginID`​ and `peelOuterOrigin`​members

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -237,13 +357,24 @@ void FactsGenerator::handleGSLPointerConstruction(const 
CXXConstructExpr *CCE) {
   assert(isGslPointerType(CCE->getType()));
   if (CCE->getNumArgs() != 1)
 return;
-  if (hasOrigin(CCE->getArg(0)))
-killAndFlowOrigin(*CCE, *CCE->getArg(0));
-  else
+
+  if (isGslPointerType(CCE->getArg(0)->getType())) {
+OriginTree *ArgTree = getTree(*CCE->getArg(0));
+assert(ArgTree && "GSL pointer argument should have an origin tree");
+// GSL pointer is constructed from another gsl pointer.
+// Example:
+//  View(View v);
+//  View(const View &v);
+if (ArgTree->getDepth() == 2)

usx95 wrote:

Makes sense. Changed to 

```
if (Arg->isGLValue())

```

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -28,21 +29,30 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, 
OriginID ID) {
 
 /// An Origin is a symbolic identifier that represents the set of possible
 /// loans a pointer-like object could hold at any given time.
-/// TODO: Enhance the origin model to handle complex types, pointer
-/// indirection and reborrowing. The plan is to move from a single origin per
-/// variable/expression to a "list of origins" governed by the Type.
-/// For example, the type 'int**' would have two origins.
-/// See discussion:
-/// 
https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
+///
+/// Each Origin corresponds to a single level of indirection. For complex types
+/// with multiple levels of indirection (e.g., `int**`), multiple Origins are
+/// organized into an OriginTree structure (see below).
 struct Origin {
   OriginID ID;
   /// A pointer to the AST node that this origin represents. This union
   /// distinguishes between origins from declarations (variables or parameters)
   /// and origins from expressions.
   llvm::PointerUnion Ptr;
 
-  Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
-  Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
+  /// The type at this indirection level.
+  ///
+  /// For `int** pp`:
+  ///   Root origin: QT = `int**` (what pp points to)
+  ///   Pointee origin: QT = `int*` (what *pp points to)
+  ///
+  /// Null for synthetic lvalue origins (e.g., outer origin of DeclRefExpr).
+  QualType QT;

usx95 wrote:

Done.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -64,29 +93,48 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginTree *VDTree = getTree(*VD);
+if (!VDTree)
+  continue;
+OriginTree *InitTree = getTree(*InitExpr);
+assert(InitTree && "VarDecl had origins but InitExpr did not");
+// Special handling for rvalue references initialized with xvalues.

usx95 wrote:

I think you are right. Removed this. I remember the `flow`​ was crashing 
earlier (while building LLVM) but it does not anymore.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits


@@ -64,29 +93,48 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginTree *VDTree = getTree(*VD);
+if (!VDTree)
+  continue;
+OriginTree *InitTree = getTree(*InitExpr);
+assert(InitTree && "VarDecl had origins but InitExpr did not");
+// Special handling for rvalue references initialized with xvalues.
+// For declarations like `Ranges&& r = std::move(ranges);`, the rvalue
+// reference should directly refer to the object being moved from,
+// rather than creating a new indirection level. We skip the outer
+// reference level and flow the pointee origins directly.
+if (VD->getType()->isRValueReferenceType() && InitExpr->isXValue()) {
+  flow(VDTree->Pointee, InitTree->Pointee, /*Kill=*/true);
+  continue;
+}
+flow(VDTree, InitTree, /*Kill=*/true);
+  }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references and PR values.
+  if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())

usx95 wrote:

> What is the motivation of skipping functions?

Primary reason is that function references/glvalues are uninteresting to the 
analysis and create extraneous facts.

> Can you give me an example where a DRE is a PRValue?

EnumConstants. Switched to explicitly skipping those to be clearer.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-12-02 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 67d76baa1b1aea810db9c4327dc4d97e87470369 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Tree -> List

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 133 -
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  15 +-
 .../LifetimeSafety/FactsGenerator.cpp | 316 ---
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 --
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 .../Sema/warn-lifetime-safety-dataflow.cpp| 526 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 414 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  85 ++-
 14 files changed, 1115 insertions(+), 639 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..5b5626020e772 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginList *getOriginsList(const ValueDecl &D);
+  OriginList *getOriginsList(const Expr &E);
+
+  void flow(OriginList *Dst, OriginList *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getOriginsList(D), getOriginsList(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getOriginsList(D), getOriginsList(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -127,24 +177,46 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
 const CXXNullPtrLiteralExpr *N) {
   /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
   /// pointers can use the same type of loan.
-  FactMgr.getOriginMgr().getOrCreate(*N);
+  getTree(*N);
 }
 
 void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  if (!hasOrigin(ICE))
+  OriginTree *Dest = getTree(*ICE);
+  if (!Dest)
+return;
+  OriginTree *SrcTree = getTree(*ICE->getSubExpr());
+
+  if (ICE->getCastKind() == CK_LValueToRValue) {
+// TODO: Decide what to do for x-values here.
+if (!ICE->getSubExpr()->isLValue())
+  return;
+
+assert(SrcTree && "LValue being cast to RValue has no origin tree");
+// The result of an LValue-to-RValue cast on a reference-to-pointer like
+// has the inner origin. Get rid of the outer origin.
+flow(getTree(*ICE), SrcTree->Pointee, /*Kill=*/true);

Xazax-hun wrote:

I think it might be better to have a separate function to get the inner origin 
(and peel the outer). The name of the function might clarify what is happening 
and we would no longer need the documentation. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -64,29 +93,48 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginTree *VDTree = getTree(*VD);
+if (!VDTree)
+  continue;
+OriginTree *InitTree = getTree(*InitExpr);
+assert(InitTree && "VarDecl had origins but InitExpr did not");
+// Special handling for rvalue references initialized with xvalues.
+// For declarations like `Ranges&& r = std::move(ranges);`, the rvalue
+// reference should directly refer to the object being moved from,
+// rather than creating a new indirection level. We skip the outer
+// reference level and flow the pointee origins directly.
+if (VD->getType()->isRValueReferenceType() && InitExpr->isXValue()) {
+  flow(VDTree->Pointee, InitTree->Pointee, /*Kill=*/true);
+  continue;
+}
+flow(VDTree, InitTree, /*Kill=*/true);
+  }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references and PR values.
+  if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())

Xazax-hun wrote:

What is the motivation of skipping functions? Is there some handling we are 
missing or are these always safe?

Can you give me an example where a DRE is a PRValue?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -28,21 +29,30 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, 
OriginID ID) {
 
 /// An Origin is a symbolic identifier that represents the set of possible
 /// loans a pointer-like object could hold at any given time.
-/// TODO: Enhance the origin model to handle complex types, pointer
-/// indirection and reborrowing. The plan is to move from a single origin per
-/// variable/expression to a "list of origins" governed by the Type.
-/// For example, the type 'int**' would have two origins.
-/// See discussion:
-/// 
https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
+///
+/// Each Origin corresponds to a single level of indirection. For complex types
+/// with multiple levels of indirection (e.g., `int**`), multiple Origins are
+/// organized into an OriginTree structure (see below).
 struct Origin {
   OriginID ID;
   /// A pointer to the AST node that this origin represents. This union
   /// distinguishes between origins from declarations (variables or parameters)
   /// and origins from expressions.
   llvm::PointerUnion Ptr;
 
-  Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
-  Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
+  /// The type at this indirection level.
+  ///
+  /// For `int** pp`:
+  ///   Root origin: QT = `int**` (what pp points to)
+  ///   Pointee origin: QT = `int*` (what *pp points to)
+  ///
+  /// Null for synthetic lvalue origins (e.g., outer origin of DeclRefExpr).
+  QualType QT;

Xazax-hun wrote:

Is the qualifier important for the loans? If not, I wonder if having a `const 
Type*` directly would be better. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -237,13 +357,24 @@ void FactsGenerator::handleGSLPointerConstruction(const 
CXXConstructExpr *CCE) {
   assert(isGslPointerType(CCE->getType()));
   if (CCE->getNumArgs() != 1)
 return;
-  if (hasOrigin(CCE->getArg(0)))
-killAndFlowOrigin(*CCE, *CCE->getArg(0));
-  else
+
+  if (isGslPointerType(CCE->getArg(0)->getType())) {
+OriginTree *ArgTree = getTree(*CCE->getArg(0));
+assert(ArgTree && "GSL pointer argument should have an origin tree");
+// GSL pointer is constructed from another gsl pointer.
+// Example:
+//  View(View v);
+//  View(const View &v);
+if (ArgTree->getDepth() == 2)

Xazax-hun wrote:

I am not sure if having a hard-coded depth here is a good idea. Would this work 
with nested views?

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -64,29 +93,48 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
 if (const auto *VD = dyn_cast(D))
-  if (hasOrigin(VD))
-if (const Expr *InitExpr = VD->getInit())
-  killAndFlowOrigin(*VD, *InitExpr);
+  if (const Expr *InitExpr = VD->getInit()) {
+OriginTree *VDTree = getTree(*VD);
+if (!VDTree)
+  continue;
+OriginTree *InitTree = getTree(*InitExpr);
+assert(InitTree && "VarDecl had origins but InitExpr did not");
+// Special handling for rvalue references initialized with xvalues.

Xazax-hun wrote:

What about regular (non-rvalue) references? I am not sure I understand why is 
this case more special than that. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits


@@ -210,11 +317,24 @@ void FactsGenerator::VisitInitListExpr(const InitListExpr 
*ILE) {
 
 void FactsGenerator::VisitMaterializeTemporaryExpr(
 const MaterializeTemporaryExpr *MTE) {
-  if (!hasOrigin(MTE))
+  OriginTree *MTETree = getTree(*MTE);
+  OriginTree *SubExprTree = getTree(*MTE->getSubExpr());
+  if (!MTETree)
 return;
-  // A temporary object's origin is the same as the origin of the
-  // expression that initializes it.
-  killAndFlowOrigin(*MTE, *MTE->getSubExpr());
+  if (MTE->isGLValue()) {
+assert(!SubExprTree ||
+   MTETree->getDepth() == SubExprTree->getDepth() + 1 && "todo doc.");
+// Issue a loan to the MTE.
+// const Loan *L = createLoan(FactMgr, MTE);

Xazax-hun wrote:

Nit: commented out code. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun edited 
https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun commented:

I just did a very rough first pass, will need to take a second look. I wonder 
if it is a good idea to call it an `OriginTree` when it is actually a list. I 
understand that it might become a tree later once/if we have named lifetimes, 
but I wonder if it is a bit confusing to call it a tree in the interim period. 

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 3e3ccdcb0a75624005c1e3599b90debbc0495d4c Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 128 -
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  15 +-
 .../LifetimeSafety/FactsGenerator.cpp | 324 ---
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 --
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 .../Sema/warn-lifetime-safety-dataflow.cpp| 526 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 414 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  85 ++-
 14 files changed, 1117 insertions(+), 640 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang-analysis

Author: Utkarsh Saxena (usx95)


Changes

This PR implements a **multi-level origin model** for the lifetime safety 
analysis, replacing the previous single-origin-per-variable approach with an 
`OriginTree` structure that captures multiple levels of indirection.

### KeyChanges

1. **OriginTree Structure**: Each origin now represents a single level of 
indirection, organized into trees:
- `int* p` → depth 2 (pointer variable + pointee)
- `int** pp` → depth 3 (variable + first pointee + second pointee)
- `std::string_view&` → depth 2 (reference + view object)
2. **Reference Type Understanding**: The analysis now properly distinguishes:
- Pointer types vs references-to-pointer types (`string_view` vs 
`string_view&`)
- References as aliases: `int a; int& b = a; int& c = b;` correctly 
recognizes both `b` and `c` alias `a`
- References don't add storage and they reuse the underlying declaration's 
origins.
3. **Type-Driven Origin Creation**: Origins are created based on type structure 
via `buildTreeForType`, ensuring origins match the type's indirection levels.
4. **Multi-Level Flow Propagation**: The `flow` function propagates origins 
through all tree levels with a critical assertion:  
This ensures type safety in origin propagation during expression handling. 
(Rant with relief: This `assert`​ was quite hard to get right but it helped 
make the right changes).
5. **Lifetimebound Semantics**: For reference return types, lifetimebound now 
propagates only the outermost origin, not inner pointee origins.

We are also deleting many tests in lifetime-safety-dataflow.cpp related to 
control flow which are better tested in unit tests and the other lit test.

---

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


14 Files Affected:

- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
(+7-6) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+7-10) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+2-2) 
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
(+104-24) 
- (modified) clang/lib/Analysis/LifetimeSafety/Facts.cpp (+11-4) 
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+231-90) 
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp (+27-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp (+13-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp (+2-1) 
- (modified) clang/lib/Analysis/LifetimeSafety/Origins.cpp (+113-58) 
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+3) 
- (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+154-372) 
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+384-30) 
- (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+55-30) 


``diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyse

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Utkarsh Saxena (usx95)


Changes

This PR implements a **multi-level origin model** for the lifetime safety 
analysis, replacing the previous single-origin-per-variable approach with an 
`OriginTree` structure that captures multiple levels of indirection.

### KeyChanges

1. **OriginTree Structure**: Each origin now represents a single level of 
indirection, organized into trees:
- `int* p` → depth 2 (pointer variable + pointee)
- `int** pp` → depth 3 (variable + first pointee + second pointee)
- `std::string_view&` → depth 2 (reference + view object)
2. **Reference Type Understanding**: The analysis now properly distinguishes:
- Pointer types vs references-to-pointer types (`string_view` vs 
`string_view&`)
- References as aliases: `int a; int& b = a; int& c = b;` correctly 
recognizes both `b` and `c` alias `a`
- References don't add storage and they reuse the underlying declaration's 
origins.
3. **Type-Driven Origin Creation**: Origins are created based on type structure 
via `buildTreeForType`, ensuring origins match the type's indirection levels.
4. **Multi-Level Flow Propagation**: The `flow` function propagates origins 
through all tree levels with a critical assertion:  
This ensures type safety in origin propagation during expression handling. 
(Rant with relief: This `assert`​ was quite hard to get right but it helped 
make the right changes).
5. **Lifetimebound Semantics**: For reference return types, lifetimebound now 
propagates only the outermost origin, not inner pointee origins.

We are also deleting many tests in lifetime-safety-dataflow.cpp related to 
control flow which are better tested in unit tests and the other lit test.

---

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


14 Files Affected:

- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
(+7-6) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+7-10) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+2-2) 
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
(+104-24) 
- (modified) clang/lib/Analysis/LifetimeSafety/Facts.cpp (+11-4) 
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+231-90) 
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp (+27-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp (+13-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp (+2-1) 
- (modified) clang/lib/Analysis/LifetimeSafety/Origins.cpp (+113-58) 
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+3) 
- (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+154-372) 
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+384-30) 
- (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+55-30) 


``diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/Lifetim

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 ready_for_review 
https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From f6223840d5e44fd7f57354ecbeb7df38012c8588 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 128 -
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  15 +-
 .../LifetimeSafety/FactsGenerator.cpp | 321 ---
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 --
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 .../Sema/warn-lifetime-safety-dataflow.cpp| 526 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 414 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  85 ++-
 14 files changed, 1113 insertions(+), 641 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread via cfe-commits


@@ -155,21 +207,35 @@ void FactsGenerator::VisitUnaryOperator(const 
UnaryOperator *UO) {
 
 void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
   if (const Expr *RetExpr = RS->getRetValue()) {
-if (hasOrigin(RetExpr)) {
-  OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
-  EscapesInCurrentBlock.push_back(
-  FactMgr.createFact(OID, RetExpr));
-}
+if (OriginTree *Tree = getTree(*RetExpr))
+  for (OriginTree *T = Tree; T; T = T->Pointee)
+EscapesInCurrentBlock.push_back(
+FactMgr.createFact(T->OID, RetExpr));
   }
 }
 
 void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
-  if (BO->isAssignmentOp())
-handleAssignment(BO->getLHS(), BO->getRHS());
+  if (BO->isCompoundAssignmentOp())
+return;
+  if (BO->isAssignmentOp()) {
+const Expr *LHSExpr = BO->getLHS();
+const Expr *RHSExpr = BO->getRHS();
+
+if (const auto *DRE_LHS =
+dyn_cast(LHSExpr->IgnoreParenImpCasts())) {
+  OriginTree *LHSTree = getTree(*DRE_LHS);
+  OriginTree *RHSTree = getTree(*RHSExpr);
+  // TODO: Handle reference types.
+  markUseAsWrite(DRE_LHS);
+  // Kill the old loans of the destination origin and flow the new loans
+  // from the source origin.
+  flow(LHSTree->Pointee, RHSTree, /*Kill=*/true);

graphite-app[bot] wrote:

Missing null checks before dereferencing origin trees. Both `LHSTree` and 
`RHSTree` could be null (if the expressions don't have origins), and 
`LHSTree->Pointee` could also be null (for depth 1 trees). This will cause null 
pointer dereferences.

```cpp
OriginTree *LHSTree = getTree(*DRE_LHS);
OriginTree *RHSTree = getTree(*RHSExpr);
if (!LHSTree || !RHSTree || !LHSTree->Pointee)
  return;  // or continue, depending on control flow
markUseAsWrite(DRE_LHS);
flow(LHSTree->Pointee, RHSTree, /*Kill=*/true);
```
```suggestion
if (const auto *DRE_LHS =
dyn_cast(LHSExpr->IgnoreParenImpCasts())) {
  OriginTree *LHSTree = getTree(*DRE_LHS);
  OriginTree *RHSTree = getTree(*RHSExpr);
  if (!LHSTree || !RHSTree || !LHSTree->Pointee)
return;
  // TODO: Handle reference types.
  markUseAsWrite(DRE_LHS);
  // Kill the old loans of the destination origin and flow the new loans
  // from the source origin.
  flow(LHSTree->Pointee, RHSTree, /*Kill=*/true);
```
  

*Spotted by [Graphite 
Agent](https://app.graphite.com/diamond/?org=llvm&ref=ai-review-comment)*https://app.graphite.com/github/pr/llvm/llvm-project/168344?chatWithGeneratedComment=b1c5f64c-1588-48c3-9435-a96d04d39715";>https://static.graphite.dev/github-diamond-fix-in-graphite-dark.svg";>https://static.graphite.dev/github-diamond-fix-in-graphite-light.svg";>https://static.graphite.dev/github-diamond-fix-in-graphite-dark.svg";>Is this helpful? React 👍 or 👎 to let us 
know.

https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/168344
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 0ab5258cf58bc214b254dc612b23613ef416e9c0 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 122 +++-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  15 +-
 .../LifetimeSafety/FactsGenerator.cpp | 303 +++---
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 --
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 .../Sema/warn-lifetime-safety-dataflow.cpp| 526 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 414 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 ++-
 14 files changed, 1090 insertions(+), 641 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/An

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 82640a94c86635c939bc66a5e6bae35d4d3f146b Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 122 +-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 303 +
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 169 ---
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/test/Sema/warn-lifetime-safety.cpp  | 414 --
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 ++--
 13 files changed, 927 insertions(+), 266 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b34a7f18b5809..a8

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 820b0b67e33d6936a610fcca0ee5f74456fe2b55 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 113 -
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 303 ++
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 +---
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/test/Sema/warn-lifetime-safety.cpp  | 387 --
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 ++--
 13 files changed, 898 insertions(+), 261 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b34a7f18b5809..a

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From c59ed89c06edc5e664b88f787e1c4521aa0c749a Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 113 --
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 303 +-
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 +---
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/test/Sema/warn-lifetime-safety.cpp  | 368 --
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 ++--
 13 files changed, 881 insertions(+), 259 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b34a7f18b5809..

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From cdd628f02cc20929cd1b54b3d2c6a7791bc817ac Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:33:33 +
Subject: [PATCH] Multi-origin changes

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 113 +--
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 303 +-
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/test/Sema/warn-lifetime-safety.cpp  | 298 +++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 +++--
 13 files changed, 811 insertions(+), 259 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b34a7f18

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 879b4353a2b410541e5199edc531d3e5d323fad6 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Wed, 26 Nov 2025 08:32:29 +
Subject: [PATCH 1/2] Annotation changes

---
 .../LifetimeSafety/LifetimeAnnotations.h  |  5 ++
 .../LifetimeSafety/FactsGenerator.cpp | 12 
 .../LifetimeSafety/LifetimeAnnotations.cpp| 31 +
 clang/lib/Sema/CheckExprLifetime.cpp  | 69 +--
 clang/lib/Sema/CheckExprLifetime.h|  3 -
 clang/lib/Sema/SemaAttr.cpp   |  4 +-
 6 files changed, 57 insertions(+), 67 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index f02969e0a9563..1a16fb82f9a84 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -38,6 +38,11 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl 
*CMD);
 /// method or because it's a normal assignment operator.
 bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
 
+// Tells whether the type is annotated with [[gsl::Pointer]].
+bool isGslPointerType(QualType QT);
+// Tells whether the type is annotated with [[gsl::Owner]].
+bool isGslOwnerType(QualType QT);
+
 } // namespace clang::lifetimes
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index f7be472ed15b5..00870c3fd4086 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -15,18 +15,6 @@
 namespace clang::lifetimes::internal {
 using llvm::isa_and_present;
 
-static bool isGslPointerType(QualType QT) {
-  if (const auto *RD = QT->getAsCXXRecordDecl()) {
-// We need to check the template definition for specializations.
-if (auto *CTSD = dyn_cast(RD))
-  return CTSD->getSpecializedTemplate()
-  ->getTemplatedDecl()
-  ->hasAttr();
-return RD->hasAttr();
-  }
-  return false;
-}
-
 static bool isPointerType(QualType QT) {
   return QT->isPointerOrReferenceType() || isGslPointerType(QT);
 }
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index ad61a42c0eaeb..54e343fc2ee5e 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -10,6 +10,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 
@@ -70,4 +71,34 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl 
*FD) {
   return isNormalAssignmentOperator(FD);
 }
 
+template  static bool isRecordWithAttr(QualType Type) {
+  auto *RD = Type->getAsCXXRecordDecl();
+  if (!RD)
+return false;
+  // Generally, if a primary template class declaration is annotated with an
+  // attribute, all its specializations generated from template instantiations
+  // should inherit the attribute.
+  //
+  // However, since lifetime analysis occurs during parsing, we may encounter
+  // cases where a full definition of the specialization is not required. In
+  // such cases, the specialization declaration remains incomplete and lacks 
the
+  // attribute. Therefore, we fall back to checking the primary template class.
+  //
+  // Note: it is possible for a specialization declaration to have an attribute
+  // even if the primary template does not.
+  //
+  // FIXME: What if the primary template and explicit specialization
+  // declarations have conflicting attributes? We should consider diagnosing
+  // this scenario.
+  bool Result = RD->hasAttr();
+
+  if (auto *CTSD = dyn_cast(RD))
+Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr();
+
+  return Result;
+}
+
+bool isGslPointerType(QualType QT) { return isRecordWithAttr(QT); 
}
+bool isGslOwnerType(QualType QT) { return isRecordWithAttr(QT); }
+
 } // namespace clang::lifetimes
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index f9665b5e59831..c91ca751984c9 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -17,6 +17,9 @@
 #include "llvm/ADT/PointerIntPair.h"
 
 namespace clang::sema {
+using lifetimes::isGslOwnerType;
+using lifetimes::isGslPointerType;
+
 namespace {
 enum LifetimeKind {
   /// The lifetime of a temporary bound to this entity ends at the end of the
@@ -257,38 +260,8 @@ static void 
visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
   Expr *Init, ReferenceKind RK,
 

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 4505a395ea4af84fdd88494e5be3ed4e7961791a Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 113 +--
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 313 --
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 233 +++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 +++--
 18 files changed, 802 insertions(+), 325 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for u

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-28 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From b0d4c689269f1bde8b26b14b46861952c1b5a3d4 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  17 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 112 +--
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 313 --
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../LifetimeSafety/LifetimeSafety.cpp |  34 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 171 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp  |   3 +
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 213 ++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  86 +++--
 18 files changed, 783 insertions(+), 323 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for us

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-20 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From f914426c2ed8eecaa513487f828afa477ecf110d Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  13 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  19 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   4 +-
 .../Analyses/LifetimeSafety/Origins.h | 109 --
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 311 --
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../LifetimeSafety/LifetimeSafety.cpp |  15 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 147 ++---
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 127 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |   5 +-
 17 files changed, 606 insertions(+), 284 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b9cad5340c940..7e383ff4299e5 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -152,7 +152,8 @@ class ReturnOfOriginFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -160,10 +161,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -191,8 +192,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+  : OriginMgr(AC.getASTContext()) {
 BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 4c8ab3f859a49..bf19f87f9e0b7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,32 +69,24 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-19 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 5b9efab397d193c64374483d0bc7939c8a7a8326 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |   9 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  19 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/Origins.h |  96 +-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 316 +-
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 128 ---
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 127 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |   5 +-
 15 files changed, 582 insertions(+), 258 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b9cad5340c940..3ae2458ebd239 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -152,7 +152,8 @@ class ReturnOfOriginFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -160,10 +161,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 4c8ab3f859a49..bf19f87f9e0b7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,32 +69,24 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
   void handleUse(const DeclRefExpr *DRE);
 
-  void markUseAsWrite(const DeclRefExpr *DRE);
+  void markUseAsWrite(const Expr *E);
 
   FactManager &FactMgr;
   AnalysisDeclContext &AC;
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index f02969e0a9563..1a16fb82f9a84 100644
--- a/clang/include/cl

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-19 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 28ba2e166fc6401704ef24a1f3d1c46d71f4b9b8 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |   9 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  19 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/Origins.h |  65 +++-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 316 +-
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 122 ---
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 127 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |   5 +-
 15 files changed, 545 insertions(+), 258 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b9cad5340c940..3ae2458ebd239 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -152,7 +152,8 @@ class ReturnOfOriginFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -160,10 +161,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 4c8ab3f859a49..bf19f87f9e0b7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,32 +69,24 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
   void handleUse(const DeclRefExpr *DRE);
 
-  void markUseAsWrite(const DeclRefExpr *DRE);
+  void markUseAsWrite(const Expr *E);
 
   FactManager &FactMgr;
   AnalysisDeclContext &AC;
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index f02969e0a9563..1a16fb82f9a84 100644
--- a/clang/include/clan

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-19 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From e0f41363b8544245e854a3e6e13736b411abaf0b Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |   9 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  19 +-
 .../LifetimeSafety/LifetimeAnnotations.h  |   5 +
 .../Analyses/LifetimeSafety/Origins.h |  89 -
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 316 +-
 .../LifetimeSafety/LifetimeAnnotations.cpp|  31 ++
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 122 ---
 clang/lib/Sema/CheckExprLifetime.cpp  |  69 ++--
 clang/lib/Sema/CheckExprLifetime.h|   3 -
 clang/lib/Sema/SemaAttr.cpp   |   4 +-
 clang/test/Sema/warn-lifetime-safety.cpp  | 127 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |   5 +-
 15 files changed, 569 insertions(+), 258 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b9cad5340c940..3ae2458ebd239 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -152,7 +152,8 @@ class ReturnOfOriginFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -160,10 +161,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 4c8ab3f859a49..bf19f87f9e0b7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,32 +69,24 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
   void handleUse(const DeclRefExpr *DRE);
 
-  void markUseAsWrite(const DeclRefExpr *DRE);
+  void markUseAsWrite(const Expr *E);
 
   FactManager &FactMgr;
   AnalysisDeclContext &AC;
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index f02969e0a9563..1a16fb82f9a84 100644
--- a/clang/include/cla

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-19 Thread Utkarsh Saxena via cfe-commits

https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/168344

>From 43233fe663b442785209415fe7a274d36cd10473 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena 
Date: Mon, 17 Nov 2025 10:53:05 +
Subject: [PATCH] lifetime-safety-multi-origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |   9 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  19 +-
 .../Analyses/LifetimeSafety/Origins.h |  67 +++-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |   5 +-
 .../LifetimeSafety/FactsGenerator.cpp | 312 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  20 +-
 .../LifetimeSafety/LoanPropagation.cpp|   3 +-
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 143 +---
 clang/test/Sema/warn-lifetime-safety.cpp  | 127 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |   5 +-
 10 files changed, 509 insertions(+), 201 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b9cad5340c940..3ae2458ebd239 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -152,7 +152,8 @@ class ReturnOfOriginFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -160,10 +161,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-  : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef OIDs)
+  : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 4c8ab3f859a49..bf19f87f9e0b7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,32 +69,24 @@ class FactsGenerator : public 
ConstStmtVisitor {
 
   template 
   void flowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(FactMgr.createFact(
-DestOID, SrcOID, /*KillDest=*/false));
+flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template 
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-CurrentBlockFacts.push_back(
-FactMgr.createFact(DestOID, SrcOID, 
/*KillDest=*/true));
+flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
   void handleUse(const DeclRefExpr *DRE);
 
-  void markUseAsWrite(const DeclRefExpr *DRE);
+  void markUseAsWrite(const Expr *E);
 
   FactManager &FactMgr;
   AnalysisDeclContext &AC;
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 56b9010f41fa2..3f58e39b1e647 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -52,28 +52,45 @@ struct Origin {
   }
 };
 
+/// A tree of origins representing levels of indirection for pointer-like
+/// types, or a single origin for non-pointer lvalues.
+struct Orig

[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)

2025-11-19 Thread via cfe-commits

github-actions[bot] wrote:


# :penguin: Linux x64 Test Results

* 84581 tests passed
* 1087 tests skipped
* 42 tests failed

## Failed Tests
(click on a test name to see its output)

### Clang

Clang.Sema/warn-lifetime-safety-dataflow.cpp

```
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 
-internal-isystem 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include
 -nostdsysteminc -fexperimental-lifetime-safety -mllvm 
-debug-only=LifetimeFacts -Wexperimental-lifetime-safety 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
 2>&1 | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
# executed command: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 
-internal-isystem 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include
 -nostdsysteminc -fexperimental-lifetime-safety -mllvm 
-debug-only=LifetimeFacts -Wexperimental-lifetime-safety 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
# note: command had no output on stdout or stderr
# executed command: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
# .---command stderr
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:14:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] 
(Expr: DeclRefExpr))
# |   ^
# | :13:11: note: scanning from here
# |  Block B2:
# |   ^
# | :16:2: note: possible intended match here
# |  Issue (0 (Path: x), ToOrigin: 0 (Expr: DeclRefExpr(x)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:31:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] 
(Expr: DeclRefExpr))
# |   ^
# | :30:11: note: scanning from here
# |  Block B2:
# |   ^
# | :33:2: note: possible intended match here
# |  Issue (0 (Path: y), ToOrigin: 0 (Expr: DeclRefExpr(y)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:61:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: {{[0-9]+}} (Expr: 
DeclRefExpr))
# |   ^
# | :58:11: note: scanning from here
# |  Block B2:
# |   ^
# | :61:2: note: possible intended match here
# |  Issue (0 (Path: x), ToOrigin: 0 (Expr: DeclRefExpr(x)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:72:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_OBJ:[0-9]+]] (Path: obj), ToOrigin: 
[[O_DRE_OBJ:[0-9]+]] (Expr: DeclRefExpr))
# |   ^
# | :69:11: note: scanning from here
# |  Block B2:
# |   ^
# | :72:2: note: possible intended match here
# |  Issue (0 (Path: obj), ToOrigin: 0 (Expr: DeclRefExpr(obj)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:85:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_TRIVIAL_OBJ:[0-9]+]] (Path: trivial_obj), ToOrigin: 
[[O_DRE_TRIVIAL:[0-9]+]] (Expr: DeclRefExpr))
# |   ^
# | :83:11: note: scanning from here
# |  Block B2:
# |   ^
# | :86:2: note: possible intended match here
# |  Issue (0 (Path: trivial_obj), ToOrigin: 0 (Expr: DeclRefExpr(trivial_obj)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:102:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_A:[0-9]+]] (Path: a), ToOrigin: [[O_DRE_A:[0-9]+]] 
(Expr: DeclRefExpr))
# |   ^
# | :96:11: note: scanning from here
# |  Block B5:
# |   ^
# | :103:2: note: possible intended match here
# |  Issue (1 (Path: a), ToOrigin: 3 (Expr: DeclRefExpr(a)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/Sema/warn-lifetime-safety-dataflow.cpp:130:11:
 error: CHECK: expected string not found in input
# | // CHECK: Issue ([[L_V1:[0-9]+]] (Path: v1), ToOrigin: [[O_DRE_V1:[0-9]+]] 
(Expr: DeclRefExpr))
# |   ^
# | :125:11: note: scanning from here
# |  Block B6:
# |   ^
# | :128:2: note: possible intended match here
# |  Issue (0 (Path: v1), ToOrigin: 0 (Expr: DeclRefExpr(v1)))
# |  ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/tes