[clang] [LifetimeSafety] Implement multi-level origins (PR #168344)
@@ -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)
@@ -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)
@@ -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)
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)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
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)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
@@ -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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
