diff options
| author | Zhijie Wang <yesterda9@gmail.com> | 2026-04-29 00:10:40 -0700 |
|---|---|---|
| committer | Zhijie Wang <yesterda9@gmail.com> | 2026-04-29 00:10:40 -0700 |
| commit | 3261586e428aecf5226ab7d84f76c75011bd51a4 (patch) | |
| tree | 61711d6ba7248941a74e02bbac84079ea7456464 | |
| parent | 922d95aefd7a09661e546a1790285b20fe70719a (diff) | |
| download | llvm-users/aeft/tree-origin-refactor.tar.gz llvm-users/aeft/tree-origin-refactor.tar.bz2 llvm-users/aeft/tree-origin-refactor.zip | |
[LifetimeSafety][NFC] Refactor OriginList to OriginNode treeusers/aeft/tree-origin-refactor
9 files changed, 227 insertions, 206 deletions
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index 88b509e1b94d..15d8abfb5439 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -20,7 +20,6 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" -#include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" #include <cstdint> @@ -236,7 +235,7 @@ public: class UseFact : public Fact { const Expr *UseExpr; - const OriginList *OList; + const OriginNode *ONode; // 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; @@ -244,11 +243,11 @@ class UseFact : public Fact { public: static bool classof(const Fact *F) { return F->getKind() == Kind::Use; } - UseFact(const Expr *UseExpr, const OriginList *OList) - : Fact(Kind::Use), UseExpr(UseExpr), OList(OList) {} + UseFact(const Expr *UseExpr, const OriginNode *ONode) + : Fact(Kind::Use), UseExpr(UseExpr), ONode(ONode) {} - const OriginList *getUsedOrigins() const { return OList; } - void setUsedOrigins(const OriginList *NewList) { OList = NewList; } + const OriginNode *getUsedOrigins() const { return ONode; } + void setUsedOrigins(const OriginNode *NewONode) { ONode = NewONode; } 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 45b16cf1ec31..b5185ba815b0 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -57,13 +57,13 @@ public: void VisitCXXDeleteExpr(const CXXDeleteExpr *DE); private: - OriginList *getOriginsList(const ValueDecl &D); - OriginList *getOriginsList(const Expr &E); + OriginNode *getOriginNode(const ValueDecl &D); + OriginNode *getOriginNode(const Expr &E); bool hasOrigins(QualType QT) const; bool hasOrigins(const Expr *E) const; - void flow(OriginList *Dst, OriginList *Src, bool Kill); + void flow(OriginNode *Dst, OriginNode *Src, bool Kill); void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr); @@ -106,12 +106,12 @@ private: template <typename Destination, typename Source> void flowOrigin(const Destination &D, const Source &S) { - flow(getOriginsList(D), getOriginsList(S), /*Kill=*/false); + flow(getOriginNode(D), getOriginNode(S), /*Kill=*/false); } template <typename Destination, typename Source> void killAndFlowOrigin(const Destination &D, const Source &S) { - flow(getOriginsList(D), getOriginsList(S), /*Kill=*/true); + flow(getOriginNode(D), getOriginNode(S), /*Kill=*/true); } /// Checks if the expression is a `void("__lifetime_test_point_...")` cast. diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h index c2db59c57906..f2d94e99d6ac 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h @@ -36,7 +36,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) { /// /// 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 OriginList structure (see below). +/// organized into a tree structure (see below). struct Origin { OriginID ID; /// A pointer to the AST node that this origin represents. This union @@ -92,30 +92,42 @@ struct Origin { /// /// The list structure enables the analysis to track how loans flow through /// different levels of indirection when assignments and dereferences occur. -class OriginList { +/// +/// TODO: Currently list-shaped (each node has at most one pointee child). +/// Will become tree-shaped once field children are added to support +/// origin trees for records whose fields have origins. +class OriginNode { public: - OriginList(OriginID OID) : OuterOID(OID) {} + OriginNode(OriginID OID) : OID(OID) {} - OriginList *peelOuterOrigin() const { return InnerList; } - OriginID getOuterOriginID() const { return OuterOID; } + OriginNode *getPointeeChild() const { + return NumChildren ? Children[0] : nullptr; + } - void setInnerOriginList(OriginList *Inner) { InnerList = Inner; } + OriginID getOriginID() const { return OID; } - // Used for assertion checks only (to ensure origin lists have matching + void setChildren(OriginNode **Arr, unsigned N) { + assert(Children == nullptr && "children must be set at most once"); + Children = Arr; + NumChildren = N; + } + + // Used for assertion checks only (to ensure pointee chains have matching // lengths). size_t getLength() const { size_t Length = 1; - const OriginList *T = this; - while (T->InnerList) { - T = T->InnerList; + const OriginNode *T = this; + while (auto *ON = T->getPointeeChild()) { + T = ON; Length++; } return Length; } private: - OriginID OuterOID; - OriginList *InnerList = nullptr; + OriginID OID; + OriginNode **Children = nullptr; + unsigned NumChildren = 0; }; bool doesDeclHaveStorage(const ValueDecl *D); @@ -126,31 +138,31 @@ class OriginManager { public: explicit OriginManager(const AnalysisDeclContext &AC); - /// Gets or creates the OriginList for a given ValueDecl. + /// Gets or creates the OriginNode 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); + /// \returns The OriginNode, or nullptr if the type is not pointer-like. + OriginNode *getOrCreateNode(const ValueDecl *D); - /// Gets or creates the OriginList for a given Expr. + /// Gets or creates the OriginNode 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); + /// \returns The OriginNode, or nullptr for non-pointer rvalues. + OriginNode *getOrCreateNode(const Expr *E); - /// Wraps an existing OriginID in a new single-element OriginList, so a fact - /// can refer to a single level of an existing OriginList. - OriginList *createSingleOriginList(OriginID OID); + /// Wraps an existing OriginID in a new single-element OriginNode, so a fact + /// can refer to a single level of an existing OriginNode. + OriginNode *createSingleOriginNode(OriginID OID); - /// Returns the OriginList for the implicit 'this' parameter if the current + /// Returns the OriginNode for the implicit 'this' parameter if the current /// declaration is an instance method. - std::optional<OriginList *> getThisOrigins() const { return ThisOrigins; } + std::optional<OriginNode *> getThisOrigins() const { return ThisOrigins; } const Origin &getOrigin(OriginID ID) const; @@ -169,11 +181,13 @@ public: private: OriginID getNextOriginID() { return NextOriginID++; } - OriginList *createNode(const ValueDecl *D, QualType QT); - OriginList *createNode(const Expr *E, QualType QT); + OriginNode *createNode(const ValueDecl *D, QualType QT); + OriginNode *createNode(const Expr *E, QualType QT); + + void attachPointeeChild(OriginNode *Parent, OriginNode *Pointee); template <typename T> - OriginList *buildListForType(QualType QT, const T *Node); + OriginNode *buildNodeForType(QualType QT, const T *Node); void initializeThisOrigins(const Decl *D); @@ -188,10 +202,10 @@ private: /// TODO(opt): Profile and evaluate the usefulness of small buffer /// optimisation. llvm::SmallVector<Origin> AllOrigins; - llvm::BumpPtrAllocator ListAllocator; - llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList; - llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList; - std::optional<OriginList *> ThisOrigins; + llvm::BumpPtrAllocator Allocator; + llvm::DenseMap<const clang::ValueDecl *, OriginNode *> DeclToNode; + llvm::DenseMap<const clang::Expr *, OriginNode *> ExprToNode; + std::optional<OriginNode *> ThisOrigins; /// Types that are not inherently pointer-like but require origin tracking /// because of lifetime annotations (currently [[clang::lifetimebound]]) on /// functions that return them. diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index 3d7fbcdacc83..d04d3e920295 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -82,9 +82,9 @@ void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << "Use ("; size_t NumUsedOrigins = getUsedOrigins()->getLength(); size_t I = 0; - for (const OriginList *Cur = getUsedOrigins(); Cur; - Cur = Cur->peelOuterOrigin(), ++I) { - OM.dump(Cur->getOuterOriginID(), OS); + for (const OriginNode *Cur = getUsedOrigins(); Cur; + Cur = Cur->getPointeeChild(), ++I) { + OM.dump(Cur->getOriginID(), OS); if (I < NumUsedOrigins - 1) OS << ", "; } diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index efdb1a1691ae..24bf706b62ee 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -30,11 +30,12 @@ namespace clang::lifetimes::internal { using llvm::isa_and_present; -OriginList *FactsGenerator::getOriginsList(const ValueDecl &D) { - return FactMgr.getOriginMgr().getOrCreateList(&D); +OriginNode *FactsGenerator::getOriginNode(const ValueDecl &D) { + return FactMgr.getOriginMgr().getOrCreateNode(&D); } -OriginList *FactsGenerator::getOriginsList(const Expr &E) { - return FactMgr.getOriginMgr().getOrCreateList(&E); + +OriginNode *FactsGenerator::getOriginNode(const Expr &E) { + return FactMgr.getOriginMgr().getOrCreateNode(&E); } bool FactsGenerator::hasOrigins(QualType QT) const { @@ -58,7 +59,7 @@ bool FactsGenerator::hasOrigins(const Expr *E) const { /// * Level 1: pp <- p's address /// * Level 2: (*pp) <- what p points to (i.e., &x) /// - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1) -void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) { +void FactsGenerator::flow(OriginNode *Dst, OriginNode *Src, bool Kill) { if (!Dst) return; assert(Src && @@ -68,9 +69,9 @@ void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) { while (Dst && Src) { CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - Dst->getOuterOriginID(), Src->getOuterOriginID(), Kill)); - Dst = Dst->peelOuterOrigin(); - Src = Src->peelOuterOrigin(); + Dst->getOriginID(), Src->getOriginID(), Kill)); + Dst = Dst->getPointeeChild(); + Src = Src->getPointeeChild(); } } @@ -146,22 +147,22 @@ void FactsGenerator::run() { /// /// Example: For `View& v`, returns the origin of what v points to, not v's /// storage. -static OriginList *getRValueOrigins(const Expr *E, OriginList *List) { - if (!List) +static OriginNode *getRValueOrigins(const Expr *E, OriginNode *Node) { + if (!Node) return nullptr; - return E->isGLValue() ? List->peelOuterOrigin() : List; + return E->isGLValue() ? Node->getPointeeChild() : Node; } void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) { for (const Decl *D : DS->decls()) if (const auto *VD = dyn_cast<VarDecl>(D)) if (const Expr *InitExpr = VD->getInit()) { - OriginList *VDList = getOriginsList(*VD); - if (!VDList) + OriginNode *VDNode = getOriginNode(*VD); + if (!VDNode) continue; - OriginList *InitList = getOriginsList(*InitExpr); - assert(InitList && "VarDecl had origins but InitExpr did not"); - flow(VDList, InitList, /*Kill=*/true); + OriginNode *InitNode = getOriginNode(*InitExpr); + assert(InitNode && "VarDecl had origins but InitExpr did not"); + flow(VDNode, InitNode, /*Kill=*/true); } } @@ -182,13 +183,13 @@ void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) { if (doesDeclHaveStorage(DRE->getDecl())) { const Loan *L = createLoan(FactMgr, DRE); assert(L); - OriginList *List = getOriginsList(*DRE); - assert(List && + OriginNode *Node = getOriginNode(*DRE); + assert(Node && "gl-value DRE of non-pointer type should have an origin list"); // This loan specifically tracks borrowing the variable's storage location - // itself and is issued to outermost origin (List->OID). + // itself and is issued to outermost origin (Node->OID). CurrentBlockFacts.push_back( - FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID())); + FactMgr.createFact<IssueFact>(L->getID(), Node->getOriginID())); } } @@ -204,8 +205,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) { CCE->getConstructor()->isDefaulted() && CCE->getNumArgs() == 1 && hasOrigins(CCE->getType())) { const Expr *Arg = CCE->getArg(0); - if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) { - flow(getOriginsList(*CCE), ArgList, /*Kill=*/true); + if (OriginNode *ArgNode = getRValueOrigins(Arg, getOriginNode(*Arg))) { + flow(getOriginNode(*CCE), ArgNode, /*Kill=*/true); return; } } @@ -214,8 +215,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) { if (const auto *RD = CCE->getType()->getAsCXXRecordDecl(); RD && isStdCallableWrapperType(RD) && CCE->getNumArgs() == 1) { const Expr *Arg = CCE->getArg(0); - if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) { - flow(getOriginsList(*CCE), ArgList, /*Kill=*/true); + if (OriginNode *ArgNode = getRValueOrigins(Arg, getOriginNode(*Arg))) { + flow(getOriginNode(*CCE), ArgNode, /*Kill=*/true); return; } } @@ -263,14 +264,14 @@ void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) { auto *MD = ME->getMemberDecl(); if (isa<FieldDecl>(MD) && doesDeclHaveStorage(MD)) { assert(ME->isGLValue() && "Field member should be GL value"); - OriginList *Dst = getOriginsList(*ME); + OriginNode *Dst = getOriginNode(*ME); assert(Dst && "Field member should have an origin list as it is GL value"); - OriginList *Src = getOriginsList(*ME->getBase()); + OriginNode *Src = getOriginNode(*ME->getBase()); assert(Src && "Base expression should be a pointer/reference type"); // The field's glvalue (outermost origin) holds the same loans as the base // expression. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - Dst->getOuterOriginID(), Src->getOuterOriginID(), + Dst->getOriginID(), Src->getOriginID(), /*Kill=*/true)); } } @@ -284,15 +285,15 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr( const CXXNullPtrLiteralExpr *N) { /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized /// pointers can use the same type of loan. - getOriginsList(*N); + getOriginNode(*N); } void FactsGenerator::VisitCastExpr(const CastExpr *CE) { - OriginList *Dest = getOriginsList(*CE); + OriginNode *Dest = getOriginNode(*CE); if (!Dest) return; const Expr *SubExpr = CE->getSubExpr(); - OriginList *Src = getOriginsList(*SubExpr); + OriginNode *Src = getOriginNode(*SubExpr); switch (CE->getCastKind()) { case CK_LValueToRValue: @@ -304,11 +305,11 @@ void FactsGenerator::VisitCastExpr(const CastExpr *CE) { // `int *p, *q; p = q;`) should propagate the inner origin (what the pointer // points to), not the outer origin (the pointer's storage location). Strip // the outer lvalue origin. - flow(getOriginsList(*CE), getRValueOrigins(SubExpr, Src), + flow(getOriginNode(*CE), getRValueOrigins(SubExpr, Src), /*Kill=*/true); return; case CK_NullToPointer: - getOriginsList(*CE); + getOriginNode(*CE); // TODO: Flow into them a null origin. return; case CK_NoOp: @@ -326,7 +327,7 @@ void FactsGenerator::VisitCastExpr(const CastExpr *CE) { case CK_ArrayToPointerDecay: assert(Src && "Array expression should have origins as it is GL value"); CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - Dest->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true)); + Dest->getOriginID(), Src->getOriginID(), /*Kill=*/true)); return; case CK_FunctionToPointerDecay: case CK_BuiltinFnToFnPtr: @@ -360,38 +361,38 @@ void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) { void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) { if (const Expr *RetExpr = RS->getRetValue()) { - if (OriginList *List = getOriginsList(*RetExpr)) - for (OriginList *L = List; L != nullptr; L = L->peelOuterOrigin()) - EscapesInCurrentBlock.push_back(FactMgr.createFact<ReturnEscapeFact>( - L->getOuterOriginID(), RetExpr)); + if (OriginNode *Node = getOriginNode(*RetExpr)) + for (OriginNode *L = Node; L != nullptr; L = L->getPointeeChild()) + EscapesInCurrentBlock.push_back( + FactMgr.createFact<ReturnEscapeFact>(L->getOriginID(), RetExpr)); } } void FactsGenerator::handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) { LHSExpr = LHSExpr->IgnoreParenImpCasts(); - OriginList *LHSList = nullptr; + OriginNode *LHSNode = nullptr; if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) { - LHSList = getOriginsList(*DRE_LHS); - assert(LHSList && "LHS is a DRE and should have an origin list"); + LHSNode = getOriginNode(*DRE_LHS); + assert(LHSNode && "LHS is a DRE and should have an origin list"); } // Handle assignment to member fields (e.g., `this->view = s` or `view = s`). // This enables detection of dangling fields when local values escape to // fields. if (const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) { - LHSList = getOriginsList(*ME_LHS); - assert(LHSList && "LHS is a MemberExpr and should have an origin list"); + LHSNode = getOriginNode(*ME_LHS); + assert(LHSNode && "LHS is a MemberExpr and should have an origin list"); } - if (!LHSList) + if (!LHSNode) return; - OriginList *RHSList = getOriginsList(*RHSExpr); + OriginNode *RHSNode = getOriginNode(*RHSExpr); // For operator= with reference parameters (e.g., // `View& operator=(const View&)`), the RHS argument stays an lvalue, // unlike built-in assignment where LValueToRValue cast strips the outer // lvalue origin. Strip it manually to get the actual value origins being // assigned. - RHSList = getRValueOrigins(RHSExpr, RHSList); + RHSNode = getRValueOrigins(RHSExpr, RHSNode); if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) { QualType QT = DRE_LHS->getDecl()->getType(); @@ -402,12 +403,12 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr, // binding live) and a Write of the inner origins (killing the pointee's // liveness). if (UseFact *UF = UseFacts.lookup(DRE_LHS)) { - const OriginList *FullList = UF->getUsedOrigins(); - assert(FullList); - UF->setUsedOrigins(FactMgr.getOriginMgr().createSingleOriginList( - FullList->getOuterOriginID())); - if (const OriginList *InnerList = FullList->peelOuterOrigin()) { - UseFact *WriteUF = FactMgr.createFact<UseFact>(DRE_LHS, InnerList); + const OriginNode *FullNode = UF->getUsedOrigins(); + assert(FullNode); + UF->setUsedOrigins(FactMgr.getOriginMgr().createSingleOriginNode( + FullNode->getOriginID())); + if (const OriginNode *InnerNode = FullNode->getPointeeChild()) { + UseFact *WriteUF = FactMgr.createFact<UseFact>(DRE_LHS, InnerNode); WriteUF->markAsWritten(); CurrentBlockFacts.push_back(WriteUF); } @@ -416,18 +417,18 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr, } else markUseAsWrite(DRE_LHS); } - if (!RHSList) { + if (!RHSNode) { // RHS has no tracked origins (e.g., assigning a callable without origins // to std::function). Clear loans of the destination. - for (OriginList *LHSInner = LHSList->peelOuterOrigin(); LHSInner; - LHSInner = LHSInner->peelOuterOrigin()) + for (OriginNode *LHSInner = LHSNode->getPointeeChild(); LHSInner; + LHSInner = LHSInner->getPointeeChild()) CurrentBlockFacts.push_back( - FactMgr.createFact<KillOriginFact>(LHSInner->getOuterOriginID())); + FactMgr.createFact<KillOriginFact>(LHSInner->getOriginID())); return; } // Kill the old loans of the destination origin and flow the new loans // from the source origin. - flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true); + flow(LHSNode->getPointeeChild(), RHSNode, /*Kill=*/true); } void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) { @@ -570,17 +571,17 @@ void FactsGenerator::VisitCXXBindTemporaryExpr( void FactsGenerator::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *MTE) { assert(MTE->isGLValue()); - OriginList *MTEList = getOriginsList(*MTE); - if (!MTEList) + OriginNode *MTENode = getOriginNode(*MTE); + if (!MTENode) return; - OriginList *SubExprList = getOriginsList(*MTE->getSubExpr()); - assert((!SubExprList || - MTEList->getLength() == (SubExprList->getLength() + 1)) && + OriginNode *SubExprNode = getOriginNode(*MTE->getSubExpr()); + assert((!SubExprNode || + MTENode->getLength() == (SubExprNode->getLength() + 1)) && "MTE top level origin should contain a loan to the MTE itself"); - OriginList *RValMTEList = getRValueOrigins(MTE, MTEList); - flow(RValMTEList, SubExprList, /*Kill=*/true); - OriginID OuterMTEID = MTEList->getOuterOriginID(); + OriginNode *RValMTENode = getRValueOrigins(MTE, MTENode); + flow(RValMTENode, SubExprNode, /*Kill=*/true); + OriginID OuterMTEID = MTENode->getOriginID(); if (MTE->getStorageDuration() == SD_FullExpression) { // Issue a loan to MTE for the storage location represented by MTE. const Loan *L = createLoan(FactMgr, MTE); @@ -593,58 +594,58 @@ void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) { // The lambda gets a single merged origin that aggregates all captured // pointer-like origins. Currently we only need to detect whether the lambda // outlives any capture. - OriginList *LambdaList = getOriginsList(*LE); - if (!LambdaList) + OriginNode *LambdaNode = getOriginNode(*LE); + if (!LambdaNode) return; bool Kill = true; for (const Expr *Init : LE->capture_inits()) { if (!Init) continue; - OriginList *InitList = getOriginsList(*Init); - if (!InitList) + OriginNode *InitNode = getOriginNode(*Init); + if (!InitNode) continue; // FIXME: Consider flowing all origin levels once lambdas support more than // one origin. Currently only the outermost origin is flowed, so by-ref // captures like `[&p]` (where p is string_view) miss inner-level // invalidation. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - LambdaList->getOuterOriginID(), InitList->getOuterOriginID(), Kill)); + LambdaNode->getOriginID(), InitNode->getOriginID(), Kill)); Kill = false; } } void FactsGenerator::VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) { assert(ASE->isGLValue() && "Array subscript should be a GL value"); - OriginList *Dst = getOriginsList(*ASE); + OriginNode *Dst = getOriginNode(*ASE); assert(Dst && "Array subscript should have origins as it is a GL value"); - OriginList *Src = getOriginsList(*ASE->getBase()); + OriginNode *Src = getOriginNode(*ASE->getBase()); assert(Src && "Base of array subscript should have origins"); CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - Dst->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true)); + Dst->getOriginID(), Src->getOriginID(), /*Kill=*/true)); } void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) { - OriginList *NewList = getOriginsList(*NE); + OriginNode *NewNode = getOriginNode(*NE); const Loan *L = createLoan(FactMgr, NE); CurrentBlockFacts.push_back( - FactMgr.createFact<IssueFact>(L->getID(), NewList->getOuterOriginID())); + FactMgr.createFact<IssueFact>(L->getID(), NewNode->getOriginID())); - NewList = NewList->peelOuterOrigin(); + NewNode = NewNode->getPointeeChild(); - if (!NewList || !NE->getInitializer()) + if (!NewNode || !NE->getInitializer()) return; - // FIXME: OriginList is null for `new[]` initializers. Remove this `Init` + // FIXME: OriginNode is null for `new[]` initializers. Remove this `Init` // check once array origins are supported. - if (OriginList *Init = getOriginsList(*NE->getInitializer()); Init) - flow(NewList, Init, true); + if (OriginNode *Init = getOriginNode(*NE->getInitializer()); Init) + flow(NewNode, Init, true); } void FactsGenerator::VisitCXXDeleteExpr(const CXXDeleteExpr *DE) { - OriginList *List = getOriginsList(*DE->getArgument()); + OriginNode *Node = getOriginNode(*DE->getArgument()); CurrentBlockFacts.push_back( - FactMgr.createFact<InvalidateOriginFact>(List->getOuterOriginID(), DE)); + FactMgr.createFact<InvalidateOriginFact>(Node->getOriginID(), DE)); } bool FactsGenerator::escapesViaReturn(OriginID OID) const { @@ -662,8 +663,8 @@ void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) { // Expire the origin when its variable's lifetime ends to ensure liveness // doesn't persist through loop back-edges. std::optional<OriginID> ExpiredOID; - if (OriginList *List = getOriginsList(*LifetimeEndsVD)) { - OriginID OID = List->getOuterOriginID(); + if (OriginNode *Node = getOriginNode(*LifetimeEndsVD)) { + OriginID OID = Node->getOriginID(); // Skip origins that escape via return; the escape checker needs their loans // to remain until the return statement is processed. if (!escapesViaReturn(OID)) @@ -704,22 +705,22 @@ void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) { const Expr *Arg = CCE->getArg(0); if (isGslPointerType(Arg->getType())) { - OriginList *ArgList = getOriginsList(*Arg); - assert(ArgList && "GSL pointer argument should have an origin list"); + OriginNode *ArgNode = getOriginNode(*Arg); + assert(ArgNode && "GSL pointer argument should have an origin list"); // GSL pointer is constructed from another gsl pointer. // Example: // View(View v); // View(const View &v); - ArgList = getRValueOrigins(Arg, ArgList); - flow(getOriginsList(*CCE), ArgList, /*Kill=*/true); + ArgNode = getRValueOrigins(Arg, ArgNode); + flow(getOriginNode(*CCE), ArgNode, /*Kill=*/true); } else if (Arg->getType()->isPointerType()) { // GSL pointer is constructed from a raw pointer. Flow only the outermost // raw pointer. Example: // View(const char*); // Span<int*>(const in**); - OriginList *ArgList = getOriginsList(*Arg); + OriginNode *ArgNode = getOriginNode(*Arg); CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - getOriginsList(*CCE)->getOuterOriginID(), ArgList->getOuterOriginID(), + getOriginNode(*CCE)->getOriginID(), ArgNode->getOriginID(), /*Kill=*/true)); } else { // This could be a new borrow. @@ -741,10 +742,10 @@ void FactsGenerator::handleMovedArgsInCall(const FunctionDecl *FD, // destructor runs after ownership has been transferred. if (isUniquePtrRelease(*MD)) { const Expr *UniquePtrExpr = Args[0]; - OriginList *MovedOrigins = getOriginsList(*UniquePtrExpr); + OriginNode *MovedOrigins = getOriginNode(*UniquePtrExpr); if (MovedOrigins) CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>( - UniquePtrExpr, MovedOrigins->getOuterOriginID())); + UniquePtrExpr, MovedOrigins->getOriginID())); } } @@ -755,12 +756,12 @@ void FactsGenerator::handleMovedArgsInCall(const FunctionDecl *FD, if (!PVD->getType()->isRValueReferenceType()) continue; const Expr *Arg = Args[I]; - OriginList *MovedOrigins = getOriginsList(*Arg); + OriginNode *MovedOrigins = getOriginNode(*Arg); assert(MovedOrigins->getLength() >= 1 && "unexpected length for r-value reference param"); // Arg is being moved to this parameter. Mark the origin as moved. - CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>( - Arg, MovedOrigins->getOuterOriginID())); + CurrentBlockFacts.push_back( + FactMgr.createFact<MovedOriginFact>(Arg, MovedOrigins->getOriginID())); } } @@ -778,10 +779,10 @@ void FactsGenerator::handleInvalidatingCall(const Expr *Call, if (!DRE || DRE->getDecl()->getType()->isReferenceType()) return; - OriginList *ThisList = getOriginsList(*Args[0]); - if (ThisList) + OriginNode *ThisNode = getOriginNode(*Args[0]); + if (ThisNode) CurrentBlockFacts.push_back(FactMgr.createFact<InvalidateOriginFact>( - ThisList->getOuterOriginID(), Call)); + ThisNode->getOriginID(), Call)); } void FactsGenerator::handleImplicitObjectFieldUses(const Expr *Call, @@ -803,9 +804,9 @@ void FactsGenerator::handleImplicitObjectFieldUses(const Expr *Call, const auto UseFields = [&](const CXXRecordDecl *RD) { for (const auto *Field : RD->fields()) - if (auto *FieldList = getOriginsList(*Field)) + if (auto *FieldNode = getOriginNode(*Field)) CurrentBlockFacts.push_back( - FactMgr.createFact<UseFact>(Call, FieldList)); + FactMgr.createFact<UseFact>(Call, FieldNode)); }; UseFields(ClassDecl); @@ -820,7 +821,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, const FunctionDecl *FD, ArrayRef<const Expr *> Args, bool IsGslConstruction) { - OriginList *CallList = getOriginsList(*Call); + OriginNode *CallNode = getOriginNode(*Call); // Ignore functions returning values with no origin. FD = getDeclWithMergedLifetimeBoundAttrs(FD); if (!FD) @@ -831,7 +832,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, handleInvalidatingCall(Call, FD, Args); handleMovedArgsInCall(FD, Args); handleImplicitObjectFieldUses(Call, FD); - if (!CallList) + if (!CallNode) return; auto IsArgLifetimeBound = [FD](unsigned I) -> bool { const ParmVarDecl *PVD = nullptr; @@ -867,23 +868,22 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, return; bool KillSrc = true; for (unsigned I = 0; I < Args.size(); ++I) { - OriginList *ArgList = getOriginsList(*Args[I]); - if (!ArgList) + OriginNode *ArgNode = getOriginNode(*Args[I]); + if (!ArgNode) 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); + assert(!Args[I]->isGLValue() || ArgNode->getLength() >= 2); + ArgNode = getRValueOrigins(Args[I], ArgNode); } if (isGslOwnerType(Args[I]->getType())) { // The constructed gsl::Pointer borrows from the Owner's storage, not // from what the Owner itself borrows, so only the outermost origin is // needed. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), - KillSrc)); + CallNode->getOriginID(), ArgNode->getOriginID(), KillSrc)); KillSrc = false; } else if (IsArgLifetimeBound(I)) { // Only flow the outer origin here. For lifetimebound args in @@ -893,24 +893,23 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, // FIXME: Handle origin-shape mismatches gracefully so we can also flow // inner origins. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), - KillSrc)); + CallNode->getOriginID(), ArgNode->getOriginID(), KillSrc)); KillSrc = false; } } else if (shouldTrackPointerImplicitObjectArg(I)) { - assert(ArgList->getLength() >= 2 && + assert(ArgNode->getLength() >= 2 && "Object arg of pointer type should have atleast two origins"); // See through the GSLPointer reference to see the pointer's value. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), - ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc)); + CallNode->getOriginID(), ArgNode->getPointeeChild()->getOriginID(), + KillSrc)); KillSrc = false; } else if (IsArgLifetimeBound(I)) { // Lifetimebound on a non-GSL-ctor function means the returned // pointer/reference itself must not outlive the arguments. This // only constraints the top-level origin. CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); + CallNode->getOriginID(), ArgNode->getOriginID(), KillSrc)); KillSrc = false; } } @@ -938,20 +937,20 @@ bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) { } void FactsGenerator::handleUse(const Expr *E) { - OriginList *List = getOriginsList(*E); - if (!List) + OriginNode *Node = getOriginNode(*E); + if (!Node) return; // For DeclRefExpr: Remove the outer layer of origin which borrows from the // decl directly (e.g., when this is not a reference). This is a use of the // underlying decl. if (auto *DRE = dyn_cast<DeclRefExpr>(E); DRE && !DRE->getDecl()->getType()->isReferenceType()) - List = getRValueOrigins(DRE, List); + Node = getRValueOrigins(DRE, Node); // Skip if there is no inner origin (e.g., when it is not a pointer type). - if (!List) + if (!Node) return; if (!UseFacts.contains(E)) { - UseFact *UF = FactMgr.createFact<UseFact>(E, List); + UseFact *UF = FactMgr.createFact<UseFact>(E, Node); CurrentBlockFacts.push_back(UF); UseFacts[E] = UF; } @@ -971,21 +970,21 @@ llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { llvm::SmallVector<Fact *> PlaceholderLoanFacts; if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) { - OriginList *List = *ThisOrigins; + OriginNode *Node = *ThisOrigins; const Loan *L = FactMgr.getLoanMgr().createLoan( AccessPath::Placeholder(cast<CXXMethodDecl>(FD)), /*IssuingExpr=*/nullptr); PlaceholderLoanFacts.push_back( - FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID())); + FactMgr.createFact<IssueFact>(L->getID(), Node->getOriginID())); } for (const ParmVarDecl *PVD : FD->parameters()) { - OriginList *List = getOriginsList(*PVD); - if (!List) + OriginNode *Node = getOriginNode(*PVD); + if (!Node) continue; const Loan *L = FactMgr.getLoanMgr().createLoan( AccessPath::Placeholder(PVD), /*IssuingExpr=*/nullptr); PlaceholderLoanFacts.push_back( - FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID())); + FactMgr.createFact<IssueFact>(L->getID(), Node->getOriginID())); } return PlaceholderLoanFacts; } diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp index cfbcacf04b1b..a23d7f224b4b 100644 --- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp @@ -129,9 +129,9 @@ public: /// the origin since it overwrites the value. Lattice transfer(Lattice In, const UseFact &UF) { Lattice Out = In; - for (const OriginList *Cur = UF.getUsedOrigins(); Cur; - Cur = Cur->peelOuterOrigin()) { - OriginID OID = Cur->getOuterOriginID(); + for (const OriginNode *Cur = UF.getUsedOrigins(); Cur; + Cur = Cur->getPointeeChild()) { + OriginID OID = Cur->getOriginID(); // Write kills liveness. if (UF.isWritten()) { Out = Lattice(Factory.remove(Out.LiveOrigins, OID)); diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp index adbc0458516e..5655b5d77810 100644 --- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp @@ -59,9 +59,9 @@ static llvm::BitVector computePersistentOrigins(const FactManager &FactMgr, break; } case Fact::Kind::Use: - for (const OriginList *Cur = F->getAs<UseFact>()->getUsedOrigins(); Cur; - Cur = Cur->peelOuterOrigin()) - CheckOrigin(Cur->getOuterOriginID()); + for (const OriginNode *Cur = F->getAs<UseFact>()->getUsedOrigins(); Cur; + Cur = Cur->getPointeeChild()) + CheckOrigin(Cur->getOriginID()); break; case Fact::Kind::KillOrigin: CheckOrigin(F->getAs<KillOriginFact>()->getKilledOrigin()); diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp index 37bd8a3ccc6b..8ee87f2802a9 100644 --- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp @@ -29,14 +29,14 @@ class MissingOriginCollector : public RecursiveASTVisitor<MissingOriginCollector> { public: MissingOriginCollector( - const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList, + const llvm::DenseMap<const clang::Expr *, OriginNode *> &ExprToOriginNode, const OriginManager &OM, LifetimeSafetyStats &LSStats) - : ExprToOriginList(ExprToOriginList), OM(OM), LSStats(LSStats) {} + : ExprToOriginNode(ExprToOriginNode), OM(OM), LSStats(LSStats) {} bool VisitExpr(Expr *E) { if (!OM.hasOrigins(E)) return true; // Check if we have an origin for this expression. - if (!ExprToOriginList.contains(E)) { + if (!ExprToOriginNode.contains(E)) { // No origin found: count this as missing origin. LSStats.ExprTypeToMissingOriginCount[E->getType().getTypePtr()]++; LSStats.ExprStmtClassToMissingOriginCount[std::string( @@ -46,7 +46,7 @@ public: } private: - const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList; + const llvm::DenseMap<const clang::Expr *, OriginNode *> &ExprToOriginNode; const OriginManager &OM; LifetimeSafetyStats &LSStats; }; @@ -171,61 +171,69 @@ void OriginManager::initializeThisOrigins(const Decl *D) { // 'this' does not refer to the lambda object itself. if (const CXXRecordDecl *P = MD->getParent(); P && P->isLambda()) return; - ThisOrigins = buildListForType(MD->getThisType(), MD); + ThisOrigins = buildNodeForType(MD->getThisType(), MD); } -OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) { +OriginNode *OriginManager::createNode(const ValueDecl *D, QualType QT) { OriginID NewID = getNextOriginID(); AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull()); - return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID); + return new (Allocator.Allocate<OriginNode>()) OriginNode(NewID); } -OriginList *OriginManager::createNode(const Expr *E, QualType QT) { +OriginNode *OriginManager::createNode(const Expr *E, QualType QT) { OriginID NewID = getNextOriginID(); AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull()); - return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID); + return new (Allocator.Allocate<OriginNode>()) OriginNode(NewID); } -OriginList *OriginManager::createSingleOriginList(OriginID OID) { - return new (ListAllocator.Allocate<OriginList>()) OriginList(OID); +OriginNode *OriginManager::createSingleOriginNode(OriginID OID) { + return new (Allocator.Allocate<OriginNode>()) OriginNode(OID); +} + +void OriginManager::attachPointeeChild(OriginNode *Parent, + OriginNode *Pointee) { + assert(Pointee && "pointee subtree must be non-null"); + Parent->setChildren(new (Allocator.Allocate<OriginNode *>()) + OriginNode *(Pointee), + /*N=*/1); } template <typename T> -OriginList *OriginManager::buildListForType(QualType QT, const T *Node) { - assert(hasOrigins(QT) && "buildListForType called for non-pointer type"); - OriginList *Head = createNode(Node, QT); +OriginNode *OriginManager::buildNodeForType(QualType QT, const T *Node) { + assert(hasOrigins(QT) && "buildNodeForType called for non-pointer type"); + OriginNode *Head = 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)) - Head->setInnerOriginList(buildListForType(PointeeTy, Node)); + attachPointeeChild(Head, buildNodeForType(PointeeTy, Node)); } return Head; } -OriginList *OriginManager::getOrCreateList(const ValueDecl *D) { +OriginNode *OriginManager::getOrCreateNode(const ValueDecl *D) { if (!hasOrigins(D->getType())) return nullptr; - auto It = DeclToList.find(D); - if (It != DeclToList.end()) + auto It = DeclToNode.find(D); + if (It != DeclToNode.end()) return It->second; - return DeclToList[D] = buildListForType(D->getType(), D); + return DeclToNode[D] = buildNodeForType(D->getType(), D); } -OriginList *OriginManager::getOrCreateList(const Expr *E) { +OriginNode *OriginManager::getOrCreateNode(const Expr *E) { if (auto *ParenIgnored = E->IgnoreParens(); ParenIgnored != E) - return getOrCreateList(ParenIgnored); + return getOrCreateNode(ParenIgnored); // We do not see CFG stmts for ExprWithCleanups. Simply peel them. if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E)) - return getOrCreateList(EWC->getSubExpr()); + return getOrCreateNode(EWC->getSubExpr()); if (!hasOrigins(E)) return nullptr; - auto It = ExprToList.find(E); - if (It != ExprToList.end()) + auto It = ExprToNode.find(E); + if (It != ExprToNode.end()) return It->second; QualType Type = E->getType(); @@ -244,7 +252,7 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { Field && isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts())) ReferencedDecl = Field; if (ReferencedDecl) { - OriginList *Head = nullptr; + OriginNode *Head = nullptr; // For non-reference declarations (e.g., `int* p`), the expression is an // lvalue (addressable) that can be borrowed, so we create an outer origin // for the lvalue itself, with the pointee being the declaration's list. @@ -254,15 +262,16 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { Head = createNode(E, QualType{}); // This ensures origin sharing: multiple expressions to the same // declaration share the same underlying origins. - Head->setInnerOriginList(getOrCreateList(ReferencedDecl)); + if (OriginNode *ON = getOrCreateNode(ReferencedDecl)) + attachPointeeChild(Head, ON); } else { // For reference-typed declarations (e.g., `int& r = p`) which have no // storage, the DeclRefExpr directly reuses the declaration's list since // references don't add an extra level of indirection at the expression // level. - Head = getOrCreateList(ReferencedDecl); + Head = getOrCreateNode(ReferencedDecl); } - return ExprToList[E] = Head; + return ExprToNode[E] = Head; } // If E is an lvalue , it refers to storage. We model this storage as the @@ -270,7 +279,7 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { // addressable. if (E->isGLValue() && !Type->isReferenceType()) Type = AST.getLValueReferenceType(Type); - return ExprToList[E] = buildListForType(Type, E); + return ExprToNode[E] = buildNodeForType(Type, E); } void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const { @@ -299,7 +308,7 @@ const Origin &OriginManager::getOrigin(OriginID ID) const { void OriginManager::collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats) { - MissingOriginCollector Collector(this->ExprToList, *this, LSStats); + MissingOriginCollector Collector(this->ExprToNode, *this, LSStats); Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody)); } diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp index 6cf65dd64ef8..81bda41f5758 100644 --- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp +++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp @@ -107,14 +107,14 @@ public: // This assumes the OriginManager's `get` can find an existing origin. // We might need a `find` method on OriginManager to avoid `getOrCreate` // logic in a const-query context if that becomes an issue. - OriginList *List = + OriginNode *List = const_cast<OriginManager &>(Analysis.getFactManager().getOriginMgr()) - .getOrCreateList(VD); + .getOrCreateNode(VD); if (!List) { ADD_FAILURE() << "No origin list found for Var '" << VarName << "'"; return std::nullopt; } - return List->getOuterOriginID(); + return List->getOriginID(); } std::vector<LoanID> getLoansForVar(llvm::StringRef VarName) { |
