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+ StringRef Annotation;
299+
300+ public:
301+ static bool classof (const Fact *F) { return F->getKind () == Kind::TestPoint; }
302+
303+ explicit TestPointFact (StringRef Annotation)
304+ : Fact(Kind::TestPoint), Annotation(Annotation) {}
305+
306+ StringRef 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+ StringRef Annotation = LiteralValue.drop_front (Prefix.length ());
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,28 @@ 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+ // We need this here for unique_ptr with forward declared class.
847+ LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis () = default ;
848+
849+ LifetimeSafetyAnalysis::LifetimeSafetyAnalysis (AnalysisDeclContext &AC)
850+ : AC(AC), Factory(std::make_unique<LifetimeFactory>()),
851+ FactMgr (std::make_unique<FactManager>()) {}
852+
853+ void LifetimeSafetyAnalysis::run () {
814854 llvm::TimeTraceScope TimeProfile (" LifetimeSafetyAnalysis" );
855+
856+ const CFG &Cfg = *AC.getCFG ();
815857 DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
816858 /* ShowColors=*/ true ));
817- FactManager FactMgr;
818- FactGenerator FactGen (FactMgr, AC);
859+
860+ FactGenerator FactGen (* FactMgr, AC);
819861 FactGen.run ();
820- DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr. dump (Cfg, AC));
862+ DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr-> dump (Cfg, AC));
821863
822864 // / TODO(opt): Consider optimizing individual blocks before running the
823865 // / dataflow analysis.
@@ -828,9 +870,56 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
828870 // / blocks; only Decls are visible. Therefore, loans in a block that
829871 // / never reach an Origin associated with a Decl can be safely dropped by
830872 // / the analysis.
831- LifetimeFactory Factory;
832- LoanPropagationAnalysis LoanPropagation (Cfg, AC, FactMgr, Factory);
833- LoanPropagation.run ();
834- DEBUG_WITH_TYPE (" LifetimeLoanPropagation" , LoanPropagation.dump ());
873+ LoanPropagation =
874+ std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
875+ LoanPropagation->run ();
876+ }
877+
878+ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint (OriginID OID,
879+ ProgramPoint PP) const {
880+ assert (LoanPropagation && " Analysis has not been run." );
881+ return LoanPropagation->getLoans (OID, PP);
882+ }
883+
884+ std::optional<OriginID>
885+ LifetimeSafetyAnalysis::getOriginIDForDecl (const ValueDecl *D) const {
886+ assert (FactMgr && " FactManager not initialized" );
887+ // This assumes the OriginManager's `get` can find an existing origin.
888+ // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
889+ // in a const-query context if that becomes an issue.
890+ return FactMgr->getOriginMgr ().get (*D);
891+ }
892+
893+ std::vector<LoanID>
894+ LifetimeSafetyAnalysis::getLoanIDForVar (const VarDecl *VD) const {
895+ assert (FactMgr && " FactManager not initialized" );
896+ std::vector<LoanID> Result;
897+ for (const Loan &L : FactMgr->getLoanMgr ().getLoans ())
898+ if (L.Path .D == VD)
899+ Result.push_back (L.ID );
900+ return Result;
901+ }
902+
903+ llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints () const {
904+ assert (FactMgr && " FactManager not initialized" );
905+ llvm::StringMap<ProgramPoint> AnnotationToPointMap;
906+ for (const CFGBlock *Block : *AC.getCFG ()) {
907+ for (const Fact *F : FactMgr->getFacts (Block)) {
908+ if (const auto *TPF = F->getAs <TestPointFact>()) {
909+ StringRef PointName = TPF->getAnnotation ();
910+ assert (AnnotationToPointMap.find (PointName) ==
911+ AnnotationToPointMap.end () &&
912+ " more than one test points with the same name" );
913+ AnnotationToPointMap[PointName] = F;
914+ }
915+ }
916+ }
917+ return AnnotationToPointMap;
918+ }
919+ } // namespace internal
920+
921+ void runLifetimeSafetyAnalysis (AnalysisDeclContext &AC) {
922+ internal::LifetimeSafetyAnalysis Analysis (AC);
923+ Analysis.run ();
835924}
836- } // namespace clang
925+ } // namespace clang::lifetimes
0 commit comments