[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
usx95 wrote:
Done.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
+using OriginID = ID;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct Loan {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ AccessPath Path;
+ SourceLocation IssueLoc;
+
+ Loan(LoanID id, AccessPath path, SourceLocation loc)
+ : ID(id), Path(path), IssueLoc(loc) {}
+};
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct Origin {
+ OriginID ID;
+ 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) {}
+
+ const clang::ValueDecl *getDecl() const {
+return Ptr.dyn_cast();
+ }
+ const clang::Expr *getExpr() const {
+return Ptr.dyn_cast();
+ }
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ Loan &addLoan(AccessPath path, SourceLocation loc) {
+++NextLoanID;
+AllLoans.emplace_back(NextLoanID, path, loc);
+return AllLoans.back();
+ }
+
+ const Loan &getLoan(LoanID id) const {
+assert(id.Value < AllLoans.size());
+return AllLoans[id.Value];
+ }
+ llvm::ArrayRef getLoans() const { return AllLoans; }
+
+private:
+ LoanID NextLoanID{0};
+ /// TODO(opt): Profile and evaluate the usefullness of small buffer
+ /// optimisation.
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return ++NextOriginID; }
+ Origin &addOrigin(OriginID id, const clang::ValueDecl &D) {
+AllOrigins.emplace_back(id, &D);
+return AllOrigins.back();
+ }
+ Origin &addOrigin(OriginID id, const clang::Expr &E) {
+AllOrigins.emplace_back(id, &E);
+return AllOrigins.back();
+ }
+
+ OriginID get(const Expr &E) {
+if (const auto *DRE = dyn_cast(&E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return get(*DRE->getDecl());
+}
+auto It = ExprToOriginID.find(&E);
+assert(It != ExprToOriginID.end());
+return It->second;
+ }
+
+ OriginID get(const ValueDecl &D) {
+auto It = DeclToOriginID.find(&D);
+assert(It != DeclToOriginID.end());
+return It->second;
+ }
+
+ OriginID getOrCreate(const Expr &E) {
+auto It = ExprToOriginID.find(&E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(&E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(*DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOrigin(NewID, E);
+ExprToOriginID[&E] = NewID;
+return NewID;
+ }
+
+ const Origin &getOrigin(OriginID ID) const {
+assert(ID.Value < AllOrigins.size());
+return AllOrigins[ID.Value];
+ }
+
+ llvm::ArrayRef getOrigins() const { return AllOrigins; }
+
+ OriginID getOrCreate(const Val
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
+using OriginID = ID;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct Loan {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ AccessPath Path;
+ SourceLocation IssueLoc;
+
+ Loan(LoanID id, AccessPath path, SourceLocation loc)
+ : ID(id), Path(path), IssueLoc(loc) {}
+};
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct Origin {
+ OriginID ID;
+ 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) {}
+
+ const clang::ValueDecl *getDecl() const {
+return Ptr.dyn_cast();
+ }
+ const clang::Expr *getExpr() const {
+return Ptr.dyn_cast();
+ }
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ Loan &addLoan(AccessPath path, SourceLocation loc) {
+++NextLoanID;
+AllLoans.emplace_back(NextLoanID, path, loc);
+return AllLoans.back();
+ }
+
+ const Loan &getLoan(LoanID id) const {
+assert(id.Value < AllLoans.size());
+return AllLoans[id.Value];
+ }
+ llvm::ArrayRef getLoans() const { return AllLoans; }
+
+private:
+ LoanID NextLoanID{0};
+ /// TODO(opt): Profile and evaluate the usefullness of small buffer
+ /// optimisation.
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return ++NextOriginID; }
usx95 wrote:
Made private. Thanks.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
usx95 wrote:
Aah. Good catch. We are using this as the index! Made this use post increment.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
usx95 wrote:
Thanks. Done.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 0cd187b01e61b200d92ca0b640789c1586075142 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH 1/3] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 728 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
5 files changed, 1112 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..8dbb4dc37026c
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanI
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 0cd187b01e61b200d92ca0b640789c1586075142 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH 1/2] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 728 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
5 files changed, 1112 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..8dbb4dc37026c
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanI
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
Xazax-hun wrote:
I think you could even do `using LoanID = ID;`, so no separate
definition is required for these tags.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
+using OriginID = ID;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct Loan {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ AccessPath Path;
+ SourceLocation IssueLoc;
+
+ Loan(LoanID id, AccessPath path, SourceLocation loc)
+ : ID(id), Path(path), IssueLoc(loc) {}
+};
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct Origin {
+ OriginID ID;
+ 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) {}
+
+ const clang::ValueDecl *getDecl() const {
+return Ptr.dyn_cast();
+ }
+ const clang::Expr *getExpr() const {
+return Ptr.dyn_cast();
+ }
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ Loan &addLoan(AccessPath path, SourceLocation loc) {
+++NextLoanID;
+AllLoans.emplace_back(NextLoanID, path, loc);
+return AllLoans.back();
+ }
+
+ const Loan &getLoan(LoanID id) const {
+assert(id.Value < AllLoans.size());
+return AllLoans[id.Value];
+ }
+ llvm::ArrayRef getLoans() const { return AllLoans; }
+
+private:
+ LoanID NextLoanID{0};
+ /// TODO(opt): Profile and evaluate the usefullness of small buffer
+ /// optimisation.
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return ++NextOriginID; }
Xazax-hun wrote:
Do we need this and the below APIs to be public? I'd expect users to only rely
on the higher level `get` and `getOrCreate` methods.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,753 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+struct AccessPath {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Temporary,// TODO: Handle.
+Field,// TODO: Handle like `s.y`.
+Heap, // TODO: Handle.
+ArrayElement, // TODO: Handle.
+Static, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ AccessPath(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template struct ID {
+ uint32_t Value = 0;
+
+ bool operator==(const ID &Other) const { return Value == Other.Value; }
+ bool operator!=(const ID &Other) const { return !(*this == Other); }
+ bool operator<(const ID &Other) const { return Value < Other.Value; }
+ ID &operator++() {
+++Value;
+return *this;
+ }
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+IDBuilder.AddInteger(Value);
+ }
+};
+
+template
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) {
+ return OS << ID.Value;
+}
+
+struct LoanTag {};
+struct OriginTag {};
+
+using LoanID = ID;
+using OriginID = ID;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct Loan {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ AccessPath Path;
+ SourceLocation IssueLoc;
+
+ Loan(LoanID id, AccessPath path, SourceLocation loc)
+ : ID(id), Path(path), IssueLoc(loc) {}
+};
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct Origin {
+ OriginID ID;
+ 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) {}
+
+ const clang::ValueDecl *getDecl() const {
+return Ptr.dyn_cast();
+ }
+ const clang::Expr *getExpr() const {
+return Ptr.dyn_cast();
+ }
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ Loan &addLoan(AccessPath path, SourceLocation loc) {
+++NextLoanID;
+AllLoans.emplace_back(NextLoanID, path, loc);
+return AllLoans.back();
+ }
+
+ const Loan &getLoan(LoanID id) const {
+assert(id.Value < AllLoans.size());
+return AllLoans[id.Value];
+ }
+ llvm::ArrayRef getLoans() const { return AllLoans; }
+
+private:
+ LoanID NextLoanID{0};
+ /// TODO(opt): Profile and evaluate the usefullness of small buffer
+ /// optimisation.
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return ++NextOriginID; }
+ Origin &addOrigin(OriginID id, const clang::ValueDecl &D) {
+AllOrigins.emplace_back(id, &D);
+return AllOrigins.back();
+ }
+ Origin &addOrigin(OriginID id, const clang::Expr &E) {
+AllOrigins.emplace_back(id, &E);
+return AllOrigins.back();
+ }
+
+ OriginID get(const Expr &E) {
+if (const auto *DRE = dyn_cast(&E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return get(*DRE->getDecl());
+}
+auto It = ExprToOriginID.find(&E);
+assert(It != ExprToOriginID.end());
+return It->second;
+ }
+
+ OriginID get(const ValueDecl &D) {
+auto It = DeclToOriginID.find(&D);
+assert(It != DeclToOriginID.end());
+return It->second;
+ }
+
+ OriginID getOrCreate(const Expr &E) {
+auto It = ExprToOriginID.find(&E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(&E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(*DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOrigin(NewID, E);
+ExprToOriginID[&E] = NewID;
+return NewID;
+ }
+
+ const Origin &getOrigin(OriginID ID) const {
+assert(ID.Value < AllOrigins.size());
+return AllOrigins[ID.Value];
+ }
+
+ llvm::ArrayRef getOrigins() const { return AllOrigins; }
+
+ OriginID getOrCreate(const Val
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
Xazax-hun wrote:
> Each of the entities like stack variable, temporaries, fields (of stack
> variables) would need to be handled slightly differently and this is only
> where this PathKind would be used.
I am not yet convinced that we will actually need this, but this may be because
we only have one kind implemented so far:
```
if (LoanPath.PathKind == AccessPath::Kind::StackVariable) {
if (LoanPath.D == DestructedVD) {
```
Here, I'd imagine when we issue the ExpireLoan, we know exactly what kind of
entity is expiring. The AST/CFG should tell us if it was a stack variable, a
heap location, or a temporary. After that, I'd expect that the `AccessPath`
should have all the information we need to check what Loans should expire here
(e.g., the fields of an expired loan should also expire).
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
usx95 wrote:
(obsolete)
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
usx95 wrote:
> Do we plan to have Unknown kinds?
Yes. I have a `TODO` to represent `Opaque` loans in `LoanInfo` where the
`AccessPath` is not known.
> But I am wondering what is this information used for?
The Path is primarily used to generate 2 facts: IssueLoan and Expireloan. When
we see a destructor/lifetime end, we find the loans with paths which just got
destroyed. These loans should not expire. Each of the entities like stack
variable, temporaries, fields (of stack variables) would need to be handled
slightly differently and this is only where this PathKind would be used.
> Also, sometimes we might learn more about a location during analysis. E.g.,
> in a branch if (p == q), the two pointers must have the same Kind. Or if we
> see delete p; we learned that p must be a heap location.
I do not have plans to modify the loans and facts based on the control flow or
the path. I would prefer to not consider path-sensitive information to keep the
analysis simple. Once the facts are generated, the dataflow would not generate
new facts.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
usx95 wrote:
Done.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
usx95 wrote:
SG. Added a TODO to reconsider this during performance evaluations.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
usx95 wrote:
Added a todo to profile and reconsider later.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
usx95 wrote:
Added default member init and removed ctor.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -2842,6 +2844,12 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
}
}
+ DEBUG_WITH_TYPE(
+ "ExperimentalLifetimeAnalysis", if (S.getLangOpts().CPlusPlus) {
usx95 wrote:
This is temporary to initially scope it to C++. Added a TODO to enable it later.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
usx95 wrote:
Done.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
usx95 wrote:
Aah yes. This might be confusing. Yes we are referring to the Declaration here.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
usx95 wrote:
I removed `Point` at the moment. I think this can be added later when this
actually looks useful. At the moment it is primarily mapping Facts to CFGStmt
which is not used.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 commented: Addressed few comments. https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
usx95 wrote:
Done.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 0cd187b01e61b200d92ca0b640789c1586075142 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH 1/2] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 728 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
5 files changed, 1112 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..8dbb4dc37026c
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanI
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 deleted https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -2842,6 +2844,12 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
}
}
+ DEBUG_WITH_TYPE(
+ "ExperimentalLifetimeAnalysis", if (S.getLangOpts().CPlusPlus) {
usx95 wrote:
This is temporary to initially scope it to C++. Added a TODO to enable it later.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
ymand wrote:
Like Gabor's point above, maybe a more specific name, like AccessPath?
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/ymand commented: Overall, looks very good. I would recommend you consider splitting this into multiple smaller PRs, so reviews can focus on a specific aspect. E.g. the FactGenerator and the LifetimeDataflow seems to deserve focused reviews. https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
ymand wrote:
Same question as above -- should this return a reference instead?
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
ymand wrote:
>From experience, it's better to use a proper data type for IDs. Using integers
>directly almost always causes pain later (say if you need to change the
>underlying type, or if you want to prevent arithmetic, etc.).
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
ymand wrote:
Did you consider using a `std::variant` instead?
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
ymand wrote:
Is there any situation in which you expect it valid to provide an `id` that's
not in `AllLoans`? If not, consider returning a reference instead and CHECK
failing on bad ids.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/ymand edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
ymand wrote:
Since a variable (reference) is an expression, this is bit surprising. Is the
`Variable` case referring to the variable _declaration_? If so, maybe make that
expression (like Declaration vs Expression instead)?
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
Xazax-hun wrote: > implement a warning (maybe return-stack-addr) in this patch. This patch is already big enough. But I think a small comment of how reporting will work down the line might be useful. https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
usx95 wrote: > I have one question though. I sort of expected this to require a liveness > analysis. Is this something we will need down the line? Or is that not > required for polonius? Good point. This is actually going to be a part of the error reporting. And yes, polonius does have a notion of live origins/loans. I can think of primarily two different strategies for generating diagnostics. 1. (preferred) Decide at the point where a **loan expires**: What are the origins that contain this expired loan and is still used later at some point. This would need a liveness analysis, i.e., it is an error to have a live origin which holds an expired loan. 2. Decide at the point where an **origin is used**: It is an error, if there is a use of an origin which holds an expired loan. The current dataflow results are enough to power this but we would need to remember which expired loans are already reported so that we do not error on each use of an origin holding an expired loan. I prefer approach 1 as this more directly works on the invalid memory as compared to origins carrying invalid loans to a potential use. I have left this out from the current implementation but let me know if you would found it helpful to implement a warning (maybe return-stack-addr) in this patch. https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/Xazax-hun commented: Overall, I don't have any major comments on the design, I have mostly some nits inline. https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,362 @@
+// RUN: %clang_cc1 -mllvm
-debug-only=ExperimentalLifetimeAnalysis,LifetimeFacts,LifetimeDataflow
-Wreturn-stack-address-cfg %s 2>&1 | FileCheck %s
+
+struct MyObj {
+ int id;
+ ~MyObj() {} // Non-trivial destructor
+};
+
+// Simple Local Variable Address and Return
+// CHECK-LABEL: Function: return_local_addr
+MyObj* return_local_addr() {
+ MyObj x {10};
+ MyObj* p = &x;
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue (LoanID: [[L_X:[0-9]+]], OriginID: [[O_ADDR_X:[0-9]+]])
+// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_ADDR_X]])
+ return p;
+// CHECK: AssignOrigin (DestID: [[O_RET_VAL:[0-9]+]], SrcID: [[O_P]])
+// CHECK: ReturnOfOrigin (OriginID: [[O_RET_VAL]])
+// CHECK: Expire (LoanID: [[L_X]])
+}
+// CHECK: Dataflow results:
+
+// CHECK-DAG: Origin [[O_ADDR_X]] contains Loan [[L_X]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_X]]
+// CHECK-DAG: Origin [[O_RET_VAL]] contains Loan [[L_X]]
+
+
+// Pointer Assignment and Return
+// CHECK-LABEL: Function: assign_and_return_local_addr
+// CHECK-NEXT: Block B{{[0-9]+}}:
+MyObj* assign_and_return_local_addr() {
+ MyObj y{20};
+ MyObj* ptr1 = &y;
+// CHECK: Issue (LoanID: [[L_Y:[0-9]+]], OriginID: [[O_ADDR_Y:[0-9]+]])
+// CHECK: AssignOrigin (DestID: [[O_PTR1:[0-9]+]], SrcID: [[O_ADDR_Y]])
+ MyObj* ptr2 = ptr1;
+// CHECK: AssignOrigin (DestID: [[O_PTR1_RVAL:[0-9]+]], SrcID: [[O_PTR1]])
+// CHECK: AssignOrigin (DestID: [[O_PTR2:[0-9]+]], SrcID: [[O_PTR1_RVAL]])
+ ptr2 = ptr1;
+// CHECK: AssignOrigin (DestID: [[O_PTR1_RVAL_2:[0-9]+]], SrcID: [[O_PTR1]])
+// CHECK: AssignOrigin (DestID: [[O_PTR2]], SrcID: [[O_PTR1_RVAL_2]])
+ ptr2 = ptr2; // Self assignment.
+// CHECK: AssignOrigin (DestID: [[O_PTR2_RVAL:[0-9]+]], SrcID: [[O_PTR2]])
+// CHECK: AssignOrigin (DestID: [[O_PTR2]], SrcID: [[O_PTR2_RVAL]])
+ return ptr2;
+// CHECK: AssignOrigin (DestID: [[O_PTR2_RVAL_2:[0-9]+]], SrcID: [[O_PTR2]])
+// CHECK: ReturnOfOrigin (OriginID: [[O_PTR2_RVAL_2]])
+// CHECK: Expire (LoanID: [[L_Y]])
+}
+// CHECK: Dataflow results:
+
+// CHECK-DAG: Origin [[O_ADDR_Y]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1_RVAL]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1_RVAL_2]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2_RVAL]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2_RVAL_2]] contains Loan [[L_Y]]
+
+
+// Return of Non-Pointer Type
+// CHECK-LABEL: Function: return_int_val
+// CHECK-NEXT: Block B{{[0-9]+}}:
+int return_int_val() {
+ int x = 10;
+ return x;
+}
+// CHECK-NEXT: End of Block
+// CHECK: Dataflow results:
+
+// CHECK:
+
+
+// Loan Expiration (Automatic Variable, C++)
+// CHECK-LABEL: Function: loan_expires_cpp
+// CHECK-NEXT: Block B{{[0-9]+}}:
+void loan_expires_cpp() {
+ MyObj obj{1};
+ MyObj* pObj = &obj;
+// CHECK: Issue (LoanID: [[L_OBJ:[0-9]+]], OriginID: [[O_ADDR_OBJ:[0-9]+]])
+// CHECK: AssignOrigin (DestID: [[O_POBJ:[0-9]+]], SrcID: [[O_ADDR_OBJ]])
+// CHECK: Expire (LoanID: [[L_OBJ]])
+}
+// CHECK: Dataflow results:
+
+// CHECK-DAG: Origin [[O_ADDR_OBJ]] contains Loan [[L_OBJ]]
+// CHECK-DAG: Origin [[O_POBJ]] contains Loan [[L_OBJ]]
+
+
+// FIXME: No expire for Trivial Destructors
+// CHECK-LABEL: Function: loan_expires_trivial
+// CHECK-NEXT: Block B{{[0-9]+}}:
+void loan_expires_trivial() {
+ int trivial_obj = 1;
+ int* pTrivialObj = &trivial_obj;
+// CHECK: Issue (LoanID: [[L_TRIVIAL_OBJ:[0-9]+]], OriginID:
[[O_ADDR_TRIVIAL_OBJ:[0-9]+]])
+// CHECK: AssignOrigin (DestID: [[O_PTOBJ:[0-9]+]], SrcID:
[[O_ADDR_TRIVIAL_OBJ]])
+// CHECK-NOT: Expire (LoanID: [[L_TRIVIAL_OBJ]])
+// CHECK-NEXT: End of Block
+ // FIXME: Add check for Expire once trivial destructors are handled for
expiration.
+}
+// CHECK: Dataflow results:
+
+// CHECK-DAG: Origin [[O_ADDR_TRIVIAL_OBJ]] contains Loan [[L_TRIVIAL_OBJ]]
+// CHECK-DAG: Origin [[O_PTOBJ]] contains Loan [[L_TRIVIAL_OBJ]]
+
+
+// CHECK-LABEL: Function: conditional
+void conditional(bool condition) {
+ int a = 5;
+ int b = 10;
+ int* p = nullptr;
+
+ if (condition)
+p = &a;
+ // CHECK: Issue (LoanID: [[L_A:[0-9]+]], OriginID: [[O_ADDR_A:[0-9]+]])
+ // CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_ADDR_A]])
+ else
+p = &b;
+ // CHECK: Issue (LoanID: [[L_B:[0-9]+]], OriginID: [[O_ADDR_B:[0-9]+]])
+ // CHECK: AssignOrigin (DestID: [[O_P]], SrcID: [[O_ADDR_B]])
+}
Xazax-hun wrote:
Could you add a test case where we have `int *q = p;` after the branch? I am
curios what facts are generated and how it is handled by the dataflow analysis.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/Xazax-hun edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
Xazax-hun wrote:
Nit: I wonder if `Point` is self-descriptive enough or whether this should be
`ProgramPoint`. Does not have a strong feeling.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal, path, loc);
+return AllLoans.back();
+ }
+
+ const LoanInfo *getLoanInfo(LoanID id) const {
+if (id < AllLoans.size())
+ return &AllLoans[id];
+return nullptr;
+ }
+ llvm::ArrayRef getLoanInfos() const { return AllLoans; }
+
+private:
+ LoanID NextLoanIDVal = 0;
+ llvm::SmallVector AllLoans;
+};
+
+class OriginManager {
+public:
+ OriginManager() = default;
+
+ OriginID getNextOriginID() { return NextOriginIDVal++; }
+ OriginInfo &addOriginInfo(OriginID id, const clang::ValueDecl *D) {
+assert(D != nullptr);
+AllOrigins.emplace_back(id, OriginKind::Variable, D);
+return AllOrigins.back();
+ }
+ OriginInfo &addOriginInfo(OriginID id, const clang::Expr *E) {
+assert(E != nullptr);
+AllOrigins.emplace_back(id, OriginKind::ExpressionResult, E);
+return AllOrigins.back();
+ }
+
+ OriginID getOrCreate(const Expr *E) {
+auto It = ExprToOriginID.find(E);
+if (It != ExprToOriginID.end())
+ return It->second;
+
+if (const auto *DRE = dyn_cast(E)) {
+ // Origin of DeclRefExpr is that of the declaration it refers to.
+ return getOrCreate(DRE->getDecl());
+}
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, E);
+ExprToOriginID[E] = NewID;
+return NewID;
+ }
+
+ const OriginInfo *getOriginInfo(OriginID id) const {
+if (id < AllOrigins.size())
+ return &AllOrigins[id];
+return nullptr;
+ }
+
+ llvm::ArrayRef getOriginInfos() const { return AllOrigins; }
+
+ OriginID getOrCreate(const ValueDecl *D) {
+auto It = DeclToOriginID.find(D);
+if (It != DeclToOriginID.end())
+ return It->second;
+OriginID NewID = getNextOriginID();
+addOriginInfo(NewID, D);
+DeclToOriginID[D] = NewID;
+return NewID;
+ }
+
+private:
+ OriginID NextOriginIDVal = 0;
+ llvm::SmallVector AllOrigins;
+ llvm::DenseMap DeclToOriginID;
+ llvm::DenseMap ExprToOriginID;
+};
+
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+ enum class Kind : uint8_t {
+/// A new loan is issued from a borrow expression (e.g., &x).
+Issue,
+/// A loan expires
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
Xazax-hun wrote:
Nit: Point is fairly small, we could take it by value.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
Xazax-hun wrote:
Nit: do we need this ctor? Alternatively, we could just have some default
member initializers.
https://github.com/llvm/llvm-project/pull/142313
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
llvmbot wrote: @llvm/pr-subscribers-clang Author: Utkarsh Saxena (usx95) Changes This patch introduces the initial implementation of the intra-procedural, flow-sensitive lifetime analysis for Clang, as proposed in the recent RFC: https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291 The primary goal of this initial submission is to establish the core dataflow framework and gather feedback on the overall design, fact representation, and testing strategy. The focus is on the dataflow mechanism itself rather than exhaustively covering all C++ AST edge cases, which will be addressed in subsequent patches. Key Components * **Conceptual Model:** Introduces the fundamental concepts of `Loan`, `Origin`, and `Path` to model memory borrows and the lifetime of pointers. * **Fact Generation:** A frontend pass traverses the Clang CFG to generate a representation of lifetime-relevant events, such as pointer assignments, taking an address, and variables going out of scope. * **Dataflow Lattice:** A dataflow lattice used to map each pointer's symbolic `Origin` to the set of `Loans` it may contain at any given program point. * **Fixed-Point Analysis:** A worklist-based, flow-sensitive analysis that propagates the lattice state across the CFG to a fixed point. * **Testing:** `llvm-lit` tests validate the analysis by checking the generated facts and final dataflow state. ### Next Steps *(Not covered in this PR but planned for subsequent patches)* The following functionality is planned for the upcoming patches to build upon this foundation and make the analysis usable in practice: * **Placeholder Loans:** Introduce placeholder loans to represent the lifetimes of function parameters, forming the basis for analysis involving function calls. * **Annotation and Opaque Call Handling:** Use placeholder loans to correctly model **function calls**, both by respecting `[[clang::lifetimebound]]` annotations and by conservatively handling opaque/un-annotated functions. * **Error Reporting:** Implement the final analysis phase that consumes the dataflow results to generate user-facing diagnostics. * **Strict vs. Permissive Modes:** Add the logic to support both high-confidence (permissive) and more comprehensive (strict) warning levels. * **Expanded C++ Coverage:** Broaden support for common patterns, including the lifetimes of temporary objects and pointers within aggregate types (structs/containers). * Performance benchmarking * Capping number of iterations or number of times a CFGBlock is processed.--- Patch is 41.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142313.diff 5 Files Affected: - (added) clang/include/clang/Analysis/Analyses/LifetimeSafety.h (+13) - (modified) clang/lib/Analysis/CMakeLists.txt (+1) - (added) clang/lib/Analysis/LifetimeSafety.cpp (+728) - (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+8) - (added) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+362) ``diff diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h new file mode 100644 index 0..daf24fff72b9b --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -0,0 +1,13 @@ +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H +#include "clang/AST/DeclBase.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +namespace clang { + +void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg, + AnalysisDeclContext &AC); + +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 8cd3990db4c3e..0523d92480cb3 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangAnalysis FixitUtil.cpp IntervalPartition.cpp IssueHash.cpp + LifetimeSafety.cpp LiveVariables.cpp MacroExpansionContext.cpp ObjCNoReturn.cpp diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp new file mode 100644**Performance on pathological test cases:**
The pathological case arise when we have `N` origins initially holding `N` loans and we have a cyclic assignment of these origins in a loop. The fixed point reaches after `N` iterations when all the origins contain all the loans. For `N` = 4, the test case would like like: ```cpp struct MyObj { int id; ~MyObj() {} }; void long_cycle(bool condition) { MyObj v1{1}; MyObj v2{1}; MyObj v3{1}; MyObj v4{1}; MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3; MyObj* p4 = &v4; while (condition) { MyObj* temp = p1; p1 = p2; p2 = p3; p3 = p4; p4 = temp; } } ```
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
llvmbot wrote: @llvm/pr-subscribers-clang-analysis Author: Utkarsh Saxena (usx95) Changes This patch introduces the initial implementation of the intra-procedural, flow-sensitive lifetime analysis for Clang, as proposed in the recent RFC: https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291 The primary goal of this initial submission is to establish the core dataflow framework and gather feedback on the overall design, fact representation, and testing strategy. The focus is on the dataflow mechanism itself rather than exhaustively covering all C++ AST edge cases, which will be addressed in subsequent patches. Key Components * **Conceptual Model:** Introduces the fundamental concepts of `Loan`, `Origin`, and `Path` to model memory borrows and the lifetime of pointers. * **Fact Generation:** A frontend pass traverses the Clang CFG to generate a representation of lifetime-relevant events, such as pointer assignments, taking an address, and variables going out of scope. * **Dataflow Lattice:** A dataflow lattice used to map each pointer's symbolic `Origin` to the set of `Loans` it may contain at any given program point. * **Fixed-Point Analysis:** A worklist-based, flow-sensitive analysis that propagates the lattice state across the CFG to a fixed point. * **Testing:** `llvm-lit` tests validate the analysis by checking the generated facts and final dataflow state. ### Next Steps *(Not covered in this PR but planned for subsequent patches)* The following functionality is planned for the upcoming patches to build upon this foundation and make the analysis usable in practice: * **Placeholder Loans:** Introduce placeholder loans to represent the lifetimes of function parameters, forming the basis for analysis involving function calls. * **Annotation and Opaque Call Handling:** Use placeholder loans to correctly model **function calls**, both by respecting `[[clang::lifetimebound]]` annotations and by conservatively handling opaque/un-annotated functions. * **Error Reporting:** Implement the final analysis phase that consumes the dataflow results to generate user-facing diagnostics. * **Strict vs. Permissive Modes:** Add the logic to support both high-confidence (permissive) and more comprehensive (strict) warning levels. * **Expanded C++ Coverage:** Broaden support for common patterns, including the lifetimes of temporary objects and pointers within aggregate types (structs/containers). * Performance benchmarking * Capping number of iterations or number of times a CFGBlock is processed.--- Patch is 41.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142313.diff 5 Files Affected: - (added) clang/include/clang/Analysis/Analyses/LifetimeSafety.h (+13) - (modified) clang/lib/Analysis/CMakeLists.txt (+1) - (added) clang/lib/Analysis/LifetimeSafety.cpp (+728) - (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+8) - (added) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+362) ``diff diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h new file mode 100644 index 0..daf24fff72b9b --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -0,0 +1,13 @@ +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H +#include "clang/AST/DeclBase.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +namespace clang { + +void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg, + AnalysisDeclContext &AC); + +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 8cd3990db4c3e..0523d92480cb3 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangAnalysis FixitUtil.cpp IntervalPartition.cpp IssueHash.cpp + LifetimeSafety.cpp LiveVariables.cpp MacroExpansionContext.cpp ObjCNoReturn.cpp diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp new file mod**Performance on pathological test cases:**
The pathological case arise when we have `N` origins initially holding `N` loans and we have a cyclic assignment of these origins in a loop. The fixed point reaches after `N` iterations when all the origins contain all the loans. For `N` = 4, the test case would like like: ```cpp struct MyObj { int id; ~MyObj() {} }; void long_cycle(bool condition) { MyObj v1{1}; MyObj v2{1}; MyObj v3{1}; MyObj v4{1}; MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3; MyObj* p4 = &v4; while (condition) { MyObj* temp = p1; p1 = p2; p2 = p3; p3 = p4; p4 = temp; } } ```
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 ready_for_review https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 0cd187b01e61b200d92ca0b640789c1586075142 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 728 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
5 files changed, 1112 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..8dbb4dc37026c
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,728 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From cb7bd7f42796b716779a44732a885ea871697227 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 721 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
5 files changed, 1105 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..147dacfa86869
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,721 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, inner types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLoanInfo(Path path, SourceLocation loc) {
+NextLoanIDVal++;
+AllLoans.emplace_back(NextLoanIDVal
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 942648ef075407d2c7a8e19414d604e254d53064 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 721 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../test/Sema/lifetime-safety-testcase-gen.py | 79 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
6 files changed, 1184 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..daf24fff72b9b
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
+ AnalysisDeclContext &AC);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..9405ac453df3b
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,721 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, dependent internal
+/// types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLo
[clang] Introduce intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 136238f2c94c6f7f90f4dd55d8e095e56f96f80b Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 721 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../test/Sema/lifetime-safety-testcase-gen.py | 79 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
6 files changed, 1184 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..5b33d582f7278
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &dc, const CFG &cfg,
+ AnalysisDeclContext &ac);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..9405ac453df3b
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,721 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, dependent internal
+/// types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLo
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 7b929ee264356c66f415f9e039819a89b1dac06b Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 723 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../test/Sema/lifetime-safety-testcase-gen.py | 79 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
6 files changed, 1186 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..5b33d582f7278
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &dc, const CFG &cfg,
+ AnalysisDeclContext &ac);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..12584d82c5f69
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,723 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+struct Point {
+ const clang::CFGBlock *Block;
+ /// Index into Block->Elements().
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Handle member accesseslike `s.y`.
+struct Path {
+ const clang::ValueDecl *D;
+
+ enum class Kind : uint8_t {
+StackVariable,
+Heap,// TODO: Handle.
+Field, // TODO: Handle.
+ArrayElement,// TODO: Handle.
+TemporaryObject, // TODO: Handle.
+StaticOrGlobal, // TODO: Handle.
+ };
+
+ Kind PathKind;
+
+ Path(const clang::ValueDecl *D, Kind K) : D(D), PathKind(K) {}
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is taken.
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Also represent Origins of complex types (fields, dependent internal
+/// types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanInfo &addLo
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From 668e329c693bc56ca877e0a05c3444a4d12cf73a Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 739 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../test/Sema/lifetime-safety-testcase-gen.py | 79 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
6 files changed, 1202 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..5b33d582f7278
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &dc, const CFG &cfg,
+ AnalysisDeclContext &ac);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..416a113f7190d
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,739 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+constexpr char kLifetimeFacts[] = "LifetimeFacts";
+constexpr char kLifetimeDataflow[] = "LifetimeDataflow";
+
+/// TODO: Why do we need this?
+struct Point {
+ const clang::CFGBlock *Block;
+ // Index into Block->Elements().
+ // Value is Block->size() if it's a point *after* the last element / before
+ // terminator.
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+struct Path {
+ llvm::PointerUnion Ptr;
+
+ enum class Kind : uint8_t {
+StackVariableAddress,
+HeapAllocationAddress,
+FieldAddress,
+ArrayElementAddress,
+TemporaryObjectAddress,
+StaticOrGlobalAddress,
+StringLiteralAddress
+ };
+
+ Kind PathKind;
+
+ Path(llvm::PointerUnion P,
+ Kind K)
+ : Ptr(P), PathKind(K) {}
+ /// TODO: Add accessors or other methods as needed
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// TODO: Document what are origins.
+/// TODO: Also represent Origins of complex types (fields, dependent internal
+/// types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+enum class FactKind : uint8_t { Issue, Expire, AssignOrigin, ReturnOfOrigin };
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanID getNextLoanID() { return NextLoanIDVal+
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/142313 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From e4aeb842055279d371326aa63ce1f1cca5428fa7 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena
Date: Mon, 2 Jun 2025 17:53:14 +
Subject: [PATCH] Introduce Intra-procedural lifetime analysis in Clang
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 739 ++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../test/Sema/lifetime-safety-testcase-gen.py | 74 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
6 files changed, 1197 insertions(+)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..5b33d582f7278
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &dc, const CFG &cfg,
+ AnalysisDeclContext &ac);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..416a113f7190d
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,739 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+constexpr char kLifetimeFacts[] = "LifetimeFacts";
+constexpr char kLifetimeDataflow[] = "LifetimeDataflow";
+
+/// TODO: Why do we need this?
+struct Point {
+ const clang::CFGBlock *Block;
+ // Index into Block->Elements().
+ // Value is Block->size() if it's a point *after* the last element / before
+ // terminator.
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+struct Path {
+ llvm::PointerUnion Ptr;
+
+ enum class Kind : uint8_t {
+StackVariableAddress,
+HeapAllocationAddress,
+FieldAddress,
+ArrayElementAddress,
+TemporaryObjectAddress,
+StaticOrGlobalAddress,
+StringLiteralAddress
+ };
+
+ Kind PathKind;
+
+ Path(llvm::PointerUnion P,
+ Kind K)
+ : Ptr(P), PathKind(K) {}
+ /// TODO: Add accessors or other methods as needed
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+ /// is represented as empty LoanSet
+ LoanID ID;
+ Path SourcePath;
+ SourceLocation IssueLoc;
+
+ LoanInfo(LoanID id, Path path, SourceLocation loc)
+ : ID(id), SourcePath(path), IssueLoc(loc) {}
+};
+
+enum class OriginKind : uint8_t { Variable, ExpressionResult };
+
+/// TODO: Document what are origins.
+/// TODO: Also represent Origins of complex types (fields, dependent internal
+/// types).
+struct OriginInfo {
+ OriginID ID;
+ OriginKind Kind;
+ union {
+const clang::ValueDecl *Decl;
+const clang::Expr *Expression;
+ };
+ OriginInfo(OriginID id, OriginKind kind, const clang::ValueDecl *D)
+ : ID(id), Kind(kind), Decl(D) {}
+ OriginInfo(OriginID id, OriginKind kind, const clang::Expr *E)
+ : ID(id), Kind(kind), Expression(E) {}
+};
+
+enum class FactKind : uint8_t { Issue, Expire, AssignOrigin, ReturnOfOrigin };
+
+class LoanManager {
+public:
+ LoanManager() = default;
+
+ LoanID getNextLoanID() { return NextLoanIDVal+
[clang] Introduce Intra-procedural lifetime analysis in Clang (PR #142313)
https://github.com/usx95 updated
https://github.com/llvm/llvm-project/pull/142313
>From c5756773cb0ab1701fa00e6068737ed7e1cff7d6 Mon Sep 17 00:00:00 2001
From: Kazu Hirata
Date: Sun, 11 May 2025 07:13:41 -0700
Subject: [PATCH] [clang] Use std::tie to implement operator< (NFC) (#139438)
---
.../clang/Analysis/Analyses/LifetimeSafety.h | 13 +
clang/include/clang/Driver/Compilation.h | 10 +-
clang/lib/Analysis/CMakeLists.txt | 1 +
clang/lib/Analysis/LifetimeSafety.cpp | 739 ++
clang/lib/Frontend/FrontendAction.cpp | 6 +-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 8 +
.../Checkers/TestAfterDivZeroChecker.cpp | 7 +-
.../test/Sema/lifetime-safety-testcase-gen.py | 74 ++
.../Sema/warn-lifetime-safety-dataflow.cpp| 362 +
9 files changed, 1202 insertions(+), 18 deletions(-)
create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety.h
create mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
create mode 100644 clang/test/Sema/lifetime-safety-testcase-gen.py
create mode 100644 clang/test/Sema/warn-lifetime-safety-dataflow.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
new file mode 100644
index 0..5b33d582f7278
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+namespace clang {
+
+void runLifetimeAnalysis(const DeclContext &dc, const CFG &cfg,
+ AnalysisDeclContext &ac);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIME_SAFETY_H
diff --git a/clang/include/clang/Driver/Compilation.h
b/clang/include/clang/Driver/Compilation.h
index 36ae85c424514..26781fc676832 100644
--- a/clang/include/clang/Driver/Compilation.h
+++ b/clang/include/clang/Driver/Compilation.h
@@ -90,14 +90,8 @@ class Compilation {
: TC(TC), BoundArch(BoundArch), DeviceOffloadKind(DeviceOffloadKind) {}
bool operator<(const TCArgsKey &K) const {
- if (TC < K.TC)
-return true;
- else if (TC == K.TC && BoundArch < K.BoundArch)
-return true;
- else if (TC == K.TC && BoundArch == K.BoundArch &&
- DeviceOffloadKind < K.DeviceOffloadKind)
-return true;
- return false;
+ return std::tie(TC, BoundArch, DeviceOffloadKind) <
+ std::tie(K.TC, K.BoundArch, K.DeviceOffloadKind);
}
};
std::map TCArgs;
diff --git a/clang/lib/Analysis/CMakeLists.txt
b/clang/lib/Analysis/CMakeLists.txt
index 8cd3990db4c3e..0523d92480cb3 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
FixitUtil.cpp
IntervalPartition.cpp
IssueHash.cpp
+ LifetimeSafety.cpp
LiveVariables.cpp
MacroExpansionContext.cpp
ObjCNoReturn.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp
b/clang/lib/Analysis/LifetimeSafety.cpp
new file mode 100644
index 0..416a113f7190d
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -0,0 +1,739 @@
+#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/Debug.h"
+#include
+
+namespace clang {
+namespace {
+
+constexpr char kLifetimeFacts[] = "LifetimeFacts";
+constexpr char kLifetimeDataflow[] = "LifetimeDataflow";
+
+/// TODO: Why do we need this?
+struct Point {
+ const clang::CFGBlock *Block;
+ // Index into Block->Elements().
+ // Value is Block->size() if it's a point *after* the last element / before
+ // terminator.
+ unsigned ElementIndex;
+
+ Point(const clang::CFGBlock *B = nullptr, unsigned Idx = 0)
+ : Block(B), ElementIndex(Idx) {}
+
+ bool operator==(const Point &Other) const {
+return Block == Other.Block && ElementIndex == Other.ElementIndex;
+ }
+};
+
+struct Path {
+ llvm::PointerUnion Ptr;
+
+ enum class Kind : uint8_t {
+StackVariableAddress,
+HeapAllocationAddress,
+FieldAddress,
+ArrayElementAddress,
+TemporaryObjectAddress,
+StaticOrGlobalAddress,
+StringLiteralAddress
+ };
+
+ Kind PathKind;
+
+ Path(llvm::PointerUnion P,
+ Kind K)
+ : Ptr(P), PathKind(K) {}
+ /// TODO: Add accessors or other methods as needed
+};
+
+using LoanID = uint32_t;
+using OriginID = uint32_t;
+
+struct LoanInfo {
+ /// TODO: Represent opaque loans.
+ /// TODO: Represent nullpt
