2424#include " llvm/Support/TimeProfiler.h"
2525#include < cstdint>
2626
27- namespace clang {
27+ namespace clang ::lifetimes {
28+ namespace internal {
2829namespace {
30+ template <typename Tag>
31+ inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
32+ return OS << ID.Value ;
33+ }
34+ } // namespace
2935
3036// / Represents the storage location being borrowed, e.g., a specific stack
3137// / variable.
@@ -36,32 +42,6 @@ struct AccessPath {
3642 AccessPath (const clang::ValueDecl *D) : D(D) {}
3743};
3844
39- // / A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
40- // / Used for giving ID to loans and origins.
41- template <typename Tag> struct ID {
42- uint32_t Value = 0 ;
43-
44- bool operator ==(const ID<Tag> &Other) const { return Value == Other.Value ; }
45- bool operator !=(const ID<Tag> &Other) const { return !(*this == Other); }
46- bool operator <(const ID<Tag> &Other) const { return Value < Other.Value ; }
47- ID<Tag> operator ++(int ) {
48- ID<Tag> Tmp = *this ;
49- ++Value;
50- return Tmp;
51- }
52- void Profile (llvm::FoldingSetNodeID &IDBuilder) const {
53- IDBuilder.AddInteger (Value);
54- }
55- };
56-
57- template <typename Tag>
58- inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
59- return OS << ID.Value ;
60- }
61-
62- using LoanID = ID<struct LoanTag >;
63- using OriginID = ID<struct OriginTag >;
64-
6545// / Information about a single borrow, or "Loan". A loan is created when a
6646// / reference or pointer is created.
6747struct Loan {
@@ -223,7 +203,9 @@ class Fact {
223203 // / An origin is propagated from a source to a destination (e.g., p = q).
224204 AssignOrigin,
225205 // / An origin escapes the function by flowing into the return value.
226- ReturnOfOrigin
206+ ReturnOfOrigin,
207+ // / A marker for a specific point in the code, for testing.
208+ TestPoint,
227209 };
228210
229211private:
@@ -310,6 +292,24 @@ class ReturnOfOriginFact : public Fact {
310292 }
311293};
312294
295+ // / A dummy-fact used to mark a specific point in the code for testing.
296+ // / It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
297+ class TestPointFact : public Fact {
298+ std::string Annotation;
299+
300+ public:
301+ static bool classof (const Fact *F) { return F->getKind () == Kind::TestPoint; }
302+
303+ explicit TestPointFact (std::string Annotation)
304+ : Fact(Kind::TestPoint), Annotation(std::move(Annotation)) {}
305+
306+ const std::string &getAnnotation () const { return Annotation; }
307+
308+ void dump (llvm::raw_ostream &OS) const override {
309+ OS << " TestPoint (Annotation: \" " << getAnnotation () << " \" )\n " ;
310+ }
311+ };
312+
313313class FactManager {
314314public:
315315 llvm::ArrayRef<const Fact *> getFacts (const CFGBlock *B) const {
@@ -363,6 +363,7 @@ class FactManager {
363363};
364364
365365class FactGenerator : public ConstStmtVisitor <FactGenerator> {
366+ using Base = ConstStmtVisitor<FactGenerator>;
366367
367368public:
368369 FactGenerator (FactManager &FactMgr, AnalysisDeclContext &AC)
@@ -458,6 +459,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
458459 }
459460 }
460461
462+ void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
463+ // Check if this is a test point marker. If so, we are done with this
464+ // expression.
465+ if (VisitTestPoint (FCE))
466+ return ;
467+ // Visit as normal otherwise.
468+ Base::VisitCXXFunctionalCastExpr (FCE);
469+ }
470+
461471private:
462472 // Check if a type has an origin.
463473 bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
@@ -491,6 +501,27 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
491501 }
492502 }
493503
504+ // / Checks if the expression is a `void("__lifetime_test_point_...")` cast.
505+ // / If so, creates a `TestPointFact` and returns true.
506+ bool VisitTestPoint (const CXXFunctionalCastExpr *FCE) {
507+ if (!FCE->getType ()->isVoidType ())
508+ return false ;
509+
510+ const auto *SubExpr = FCE->getSubExpr ()->IgnoreParenImpCasts ();
511+ if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
512+ llvm::StringRef LiteralValue = SL->getString ();
513+ const std::string Prefix = " __lifetime_test_point_" ;
514+
515+ if (LiteralValue.starts_with (Prefix)) {
516+ std::string Annotation = LiteralValue.drop_front (Prefix.length ()).str ();
517+ CurrentBlockFacts.push_back (
518+ FactMgr.createFact <TestPointFact>(Annotation));
519+ return true ;
520+ }
521+ }
522+ return false ;
523+ }
524+
494525 FactManager &FactMgr;
495526 AnalysisDeclContext &AC;
496527 llvm::SmallVector<Fact *> CurrentBlockFacts;
@@ -637,6 +668,8 @@ class DataflowAnalysis {
637668 return D->transfer (In, *F->getAs <AssignOriginFact>());
638669 case Fact::Kind::ReturnOfOrigin:
639670 return D->transfer (In, *F->getAs <ReturnOfOriginFact>());
671+ case Fact::Kind::TestPoint:
672+ return D->transfer (In, *F->getAs <TestPointFact>());
640673 }
641674 llvm_unreachable (" Unknown fact kind" );
642675 }
@@ -646,14 +679,16 @@ class DataflowAnalysis {
646679 Lattice transfer (Lattice In, const ExpireFact &) { return In; }
647680 Lattice transfer (Lattice In, const AssignOriginFact &) { return In; }
648681 Lattice transfer (Lattice In, const ReturnOfOriginFact &) { return In; }
682+ Lattice transfer (Lattice In, const TestPointFact &) { return In; }
649683};
650684
651685namespace utils {
652686
653687// / Computes the union of two ImmutableSets.
654688template <typename T>
655- llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A, llvm::ImmutableSet<T> B,
656- typename llvm::ImmutableSet<T>::Factory &F) {
689+ static llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A,
690+ llvm::ImmutableSet<T> B,
691+ typename llvm::ImmutableSet<T>::Factory &F) {
657692 if (A.getHeight () < B.getHeight ())
658693 std::swap (A, B);
659694 for (const T &E : B)
@@ -666,7 +701,7 @@ llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A, llvm::ImmutableSet<T> B,
666701// efficient merge could be implemented using a Patricia Trie or HAMT
667702// instead of the current AVL-tree-based ImmutableMap.
668703template <typename K, typename V, typename Joiner>
669- llvm::ImmutableMap<K, V>
704+ static llvm::ImmutableMap<K, V>
670705join (llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
671706 typename llvm::ImmutableMap<K, V>::Factory &F, Joiner joinValues) {
672707 if (A.getHeight () < B.getHeight ())
@@ -690,10 +725,6 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
690725// Loan Propagation Analysis
691726// ========================================================================= //
692727
693- // Using LLVM's immutable collections is efficient for dataflow analysis
694- // as it avoids deep copies during state transitions.
695- // TODO(opt): Consider using a bitset to represent the set of loans.
696- using LoanSet = llvm::ImmutableSet<LoanID>;
697728using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
698729
699730// / An object to hold the factories for immutable collections, ensuring
@@ -807,17 +838,27 @@ class LoanPropagationAnalysis
807838// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
808839// - Using the above three to perform the final error reporting.
809840// ========================================================================= //
810- } // anonymous namespace
811841
812- void runLifetimeSafetyAnalysis (const DeclContext &DC, const CFG &Cfg,
813- AnalysisDeclContext &AC) {
842+ // ========================================================================= //
843+ // LifetimeSafetyAnalysis Class Implementation
844+ // ========================================================================= //
845+
846+ LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis () = default ;
847+
848+ LifetimeSafetyAnalysis::LifetimeSafetyAnalysis (AnalysisDeclContext &AC)
849+ : AC(AC), Factory(std::make_unique<LifetimeFactory>()),
850+ FactMgr (std::make_unique<FactManager>()) {}
851+
852+ void LifetimeSafetyAnalysis::run () {
814853 llvm::TimeTraceScope TimeProfile (" LifetimeSafetyAnalysis" );
854+
855+ const CFG &Cfg = *AC.getCFG ();
815856 DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
816857 /* ShowColors=*/ true ));
817- FactManager FactMgr;
818- FactGenerator FactGen (FactMgr, AC);
858+
859+ FactGenerator FactGen (* FactMgr, AC);
819860 FactGen.run ();
820- DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr. dump (Cfg, AC));
861+ DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr-> dump (Cfg, AC));
821862
822863 // / TODO(opt): Consider optimizing individual blocks before running the
823864 // / dataflow analysis.
@@ -828,9 +869,49 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
828869 // / blocks; only Decls are visible. Therefore, loans in a block that
829870 // / never reach an Origin associated with a Decl can be safely dropped by
830871 // / the analysis.
831- LifetimeFactory Factory;
832- LoanPropagationAnalysis LoanPropagation (Cfg, AC, FactMgr, Factory);
833- LoanPropagation.run ();
834- DEBUG_WITH_TYPE (" LifetimeLoanPropagation" , LoanPropagation.dump ());
872+ LoanPropagation =
873+ std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
874+ LoanPropagation->run ();
875+ }
876+
877+ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint (OriginID OID,
878+ ProgramPoint PP) const {
879+ assert (LoanPropagation && " Analysis has not been run." );
880+ return LoanPropagation->getLoans (OID, PP);
881+ }
882+
883+ std::optional<OriginID>
884+ LifetimeSafetyAnalysis::getOriginIDForDecl (const ValueDecl *D) const {
885+ assert (FactMgr && " FactManager not initialized" );
886+ // This assumes the OriginManager's `get` can find an existing origin.
887+ // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
888+ // in a const-query context if that becomes an issue.
889+ return FactMgr->getOriginMgr ().get (*D);
890+ }
891+
892+ std::vector<LoanID>
893+ LifetimeSafetyAnalysis::getLoanIDForVar (const VarDecl *VD) const {
894+ assert (FactMgr && " FactManager not initialized" );
895+ std::vector<LoanID> Result;
896+ for (const Loan &L : FactMgr->getLoanMgr ().getLoans ())
897+ if (L.Path .D == VD)
898+ Result.push_back (L.ID );
899+ return Result;
900+ }
901+
902+ llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints () const {
903+ assert (FactMgr && " FactManager not initialized" );
904+ llvm::StringMap<ProgramPoint> AnnotationToPointMap;
905+ for (const CFGBlock *Block : *AC.getCFG ())
906+ for (const Fact *F : FactMgr->getFacts (Block))
907+ if (const auto *TPF = F->getAs <TestPointFact>())
908+ AnnotationToPointMap[TPF->getAnnotation ()] = F;
909+ return AnnotationToPointMap;
910+ }
911+ } // namespace internal
912+
913+ void runLifetimeSafetyAnalysis (AnalysisDeclContext &AC) {
914+ internal::LifetimeSafetyAnalysis Analysis (AC);
915+ Analysis.run ();
835916}
836- } // namespace clang
917+ } // namespace clang::lifetimes
0 commit comments