llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Utkarsh Saxena (usx95)

<details>
<summary>Changes</summary>

This PR implements a **multi-level origin model** for the lifetime safety 
analysis, replacing the previous single-origin-per-variable approach with an 
`OriginTree` structure that captures multiple levels of indirection.

### KeyChanges

1. **OriginTree Structure**: Each origin now represents a single level of 
indirection, organized into trees:
    - `int* p` → depth 2 (pointer variable + pointee)
    - `int** pp` → depth 3 (variable + first pointee + second pointee)
    - `std::string_view&amp;` → depth 2 (reference + view object)
2. **Reference Type Understanding**: The analysis now properly distinguishes:
    - Pointer types vs references-to-pointer types (`string_view` vs 
`string_view&amp;`)
    - References as aliases: `int a; int&amp; b = a; int&amp; c = b;` correctly 
recognizes both `b` and `c` alias `a`
    - References don't add storage and they reuse the underlying declaration's 
origins.
3. **Type-Driven Origin Creation**: Origins are created based on type structure 
via `buildTreeForType`, ensuring origins match the type's indirection levels.
4. **Multi-Level Flow Propagation**: The `flow` function propagates origins 
through all tree levels with a critical assertion:  
This ensures type safety in origin propagation during expression handling. 
(Rant with relief: This `assert`​ was quite hard to get right but it helped 
make the right changes).
5. **Lifetimebound Semantics**: For reference return types, lifetimebound now 
propagates only the outermost origin, not inner pointee origins.

We are also deleting many tests in lifetime-safety-dataflow.cpp related to 
control flow which are better tested in unit tests and the other lit test.

---

Patch is 98.11 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/168344.diff


14 Files Affected:

- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
(+7-6) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+7-10) 
- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+2-2) 
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
(+104-24) 
- (modified) clang/lib/Analysis/LifetimeSafety/Facts.cpp (+11-4) 
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+231-90) 
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp (+27-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp (+13-7) 
- (modified) clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp (+2-1) 
- (modified) clang/lib/Analysis/LifetimeSafety/Origins.cpp (+113-58) 
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+3) 
- (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+154-372) 
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+384-30) 
- (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+55-30) 


``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b5f7f8746186a..908d2a5b8cc76 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -155,7 +155,8 @@ class OriginEscapesFact : public Fact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  OriginID OID;
+  // The origins of the expression being used.
+  llvm::SmallVector<OriginID, 1> OIDs;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -163,10 +164,10 @@ class UseFact : public Fact {
 public:
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
-  UseFact(const Expr *UseExpr, OriginManager &OM)
-      : Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
+  UseFact(const Expr *UseExpr, llvm::ArrayRef<OriginID> OIDs)
+      : Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
 
-  OriginID getUsedOrigin() const { return OID; }
+  llvm::ArrayRef<OriginID> getUsedOrigins() const { return OIDs; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
 
 class FactManager {
 public:
-  void init(const CFG &Cfg) {
-    assert(BlockToFacts.empty() && "FactManager already initialized");
+  FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
+      : OriginMgr(AC.getASTContext()) {
     BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 878cb90b685f9..939f421505463 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,11 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
+  OriginTree *getTree(const ValueDecl &D);
+  OriginTree *getTree(const Expr &E);
+
+  void flow(OriginTree *Dst, OriginTree *Src, bool Kill);
+
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
 
   template <typename Destination, typename Source>
   void flowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-        DestOID, SrcOID, /*KillDest=*/false));
+    flow(getTree(D), getTree(S), /*Kill=*/false);
   }
 
   template <typename Destination, typename Source>
   void killAndFlowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(
-        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, 
/*KillDest=*/true));
+    flow(getTree(D), getTree(S), /*Kill=*/true);
   }
 
   /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
   /// If so, creates a `TestPointFact` and returns true.
   bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
 
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
-
   // A DeclRefExpr will be treated as a use of the referenced decl. It will be
   // checked for use-after-free unless it is later marked as being written to
   // (e.g. on the left-hand side of an assignment).
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b34a7f18b5809..a8d6e2551aab5 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -76,13 +76,13 @@ class LifetimeSafetyAnalysis {
     return *LoanPropagation;
   }
   LiveOriginsAnalysis &getLiveOrigins() const { return *LiveOrigins; }
-  FactManager &getFactManager() { return FactMgr; }
+  FactManager &getFactManager() { return *FactMgr; }
 
 private:
   AnalysisDeclContext &AC;
   LifetimeSafetyReporter *Reporter;
   LifetimeFactory Factory;
-  FactManager FactMgr;
+  std::unique_ptr<FactManager> FactMgr;
   std::unique_ptr<LiveOriginsAnalysis> LiveOrigins;
   std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
 };
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 56b9010f41fa2..c595a9fdf6233 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/TypeBase.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 
 namespace clang::lifetimes::internal {
@@ -28,12 +29,10 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, 
OriginID ID) {
 
 /// An Origin is a symbolic identifier that represents the set of possible
 /// loans a pointer-like object could hold at any given time.
-/// TODO: Enhance the origin model to handle complex types, pointer
-/// indirection and reborrowing. The plan is to move from a single origin per
-/// variable/expression to a "list of origins" governed by the Type.
-/// For example, the type 'int**' would have two origins.
-/// See discussion:
-/// 
https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
+///
+/// Each Origin corresponds to a single level of indirection. For complex types
+/// with multiple levels of indirection (e.g., `int**`), multiple Origins are
+/// organized into an OriginTree structure (see below).
 struct Origin {
   OriginID ID;
   /// A pointer to the AST node that this origin represents. This union
@@ -41,8 +40,19 @@ struct Origin {
   /// and origins from expressions.
   llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
 
-  Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
-  Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
+  /// The type at this indirection level.
+  ///
+  /// For `int** pp`:
+  ///   Root origin: QT = `int**` (what pp points to)
+  ///   Pointee origin: QT = `int*` (what *pp points to)
+  ///
+  /// Null for synthetic lvalue origins (e.g., outer origin of DeclRefExpr).
+  QualType QT;
+
+  Origin(OriginID ID, const clang::ValueDecl *D, QualType QT)
+      : ID(ID), Ptr(D), QT(QT) {}
+  Origin(OriginID ID, const clang::Expr *E, QualType QT)
+      : ID(ID), Ptr(E), QT(QT) {}
 
   const clang::ValueDecl *getDecl() const {
     return Ptr.dyn_cast<const clang::ValueDecl *>();
@@ -52,28 +62,81 @@ struct Origin {
   }
 };
 
+/// A tree of origins representing levels of indirection for pointer-like 
types.
+///
+/// Each node in the tree contains an OriginID representing a level of
+/// indirection. The tree structure captures the multi-level nature of
+/// pointer and reference types in the lifetime analysis.
+///
+/// Examples:
+///   - For `int& x`, the tree has depth 2:
+///     * Root: origin for the reference storage itself (the lvalue `x`)
+///     * Pointee: origin for what `x` refers to
+///
+///   - For `int* p`, the tree has depth 2:
+///     * Root: origin for the pointer variable `p`
+///     * Pointee: origin for what `p` points to
+///
+///   - For `View v` (where View is gsl::Pointer), the tree has depth 2:
+///     * Root: origin for the view object itself
+///     * Pointee: origin for what the view refers to
+///
+///   - For `int** pp`, the tree has depth 3:
+///     * Root: origin for `pp` itself
+///     * Pointee: origin for `*pp` (what `pp` points to)
+///     * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
+///
+/// The tree structure enables the analysis to track how loans flow through
+/// different levels of indirection when assignments and dereferences occur.
+struct OriginTree {
+  OriginID OID;
+  OriginTree *Pointee = nullptr;
+
+  OriginTree(OriginID OID) : OID(OID) {}
+
+  size_t getDepth() const {
+    size_t Depth = 1;
+    const OriginTree *T = this;
+    while (T->Pointee) {
+      T = T->Pointee;
+      Depth++;
+    }
+    return Depth;
+  }
+};
+
+bool hasOrigins(QualType QT);
+bool hasOrigins(const Expr *E);
+bool doesDeclHaveStorage(const ValueDecl *D);
+
 /// Manages the creation, storage, and retrieval of origins for pointer-like
 /// variables and expressions.
 class OriginManager {
 public:
-  OriginManager() = default;
-
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
-  Origin &addOrigin(OriginID ID, const clang::Expr &E);
-
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E);
-
-  OriginID get(const ValueDecl &D);
-
-  OriginID getOrCreate(const Expr &E);
+  explicit OriginManager(ASTContext &AST) : AST(AST) {}
+
+  /// Gets or creates the OriginTree for a given ValueDecl.
+  ///
+  /// Creates a tree structure mirroring the levels of indirection in the
+  /// declaration's type (e.g., `int** p` creates depth 2).
+  ///
+  /// \returns The OriginTree, or nullptr if the type is not pointer-like.
+  OriginTree *getOrCreateTree(const ValueDecl *D);
+
+  /// Gets or creates the OriginTree for a given Expr.
+  ///
+  /// Creates a tree based on the expression's type and value category:
+  /// - Lvalues get an implicit reference level (modeling addressability)
+  /// - Rvalues of non-pointer type return nullptr (no trackable origin)
+  /// - DeclRefExpr may reuse the underlying declaration's tree
+  ///
+  /// \returns The OriginTree, or nullptr for non-pointer rvalues.
+  OriginTree *getOrCreateTree(const Expr *E, size_t Depth = 0);
 
   const Origin &getOrigin(OriginID ID) const;
 
   llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
 
-  OriginID getOrCreate(const ValueDecl &D);
-
   unsigned getNumOrigins() const { return NextOriginID.Value; }
 
   void dump(OriginID OID, llvm::raw_ostream &OS) const;
@@ -81,12 +144,29 @@ class OriginManager {
 private:
   OriginID getNextOriginID() { return NextOriginID++; }
 
+  OriginTree *createNode(const ValueDecl *D, QualType QT) {
+    OriginID NewID = getNextOriginID();
+    AllOrigins.emplace_back(NewID, D, QT);
+    return new (TreeAllocator.Allocate<OriginTree>()) OriginTree(NewID);
+  }
+
+  OriginTree *createNode(const Expr *E, QualType QT) {
+    OriginID NewID = getNextOriginID();
+    AllOrigins.emplace_back(NewID, E, QT);
+    return new (TreeAllocator.Allocate<OriginTree>()) OriginTree(NewID);
+  }
+
+  template <typename T>
+  OriginTree *buildTreeForType(QualType QT, const T *Node);
+
+  ASTContext &AST;
   OriginID NextOriginID{0};
-  /// TODO(opt): Profile and evaluate the usefullness of small buffer
+  /// TODO(opt): Profile and evaluate the usefulness of small buffer
   /// optimisation.
   llvm::SmallVector<Origin> AllOrigins;
-  llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
-  llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
+  llvm::BumpPtrAllocator TreeAllocator;
+  llvm::DenseMap<const clang::ValueDecl *, OriginTree *> DeclToTree;
+  llvm::DenseMap<const clang::Expr *, OriginTree *> ExprToTree;
 };
 } // namespace clang::lifetimes::internal
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 0ae7111c489e8..d83f88409dcfa 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -35,12 +35,14 @@ void ExpireFact::dump(llvm::raw_ostream &OS, const 
LoanManager &LM,
 
 void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &,
                           const OriginManager &OM) const {
-  OS << "OriginFlow (Dest: ";
+  OS << "OriginFlow: \n";
+  OS << "\tDest: ";
   OM.dump(getDestOriginID(), OS);
-  OS << ", Src: ";
+  OS << "\n";
+  OS << "\tSrc:  ";
   OM.dump(getSrcOriginID(), OS);
   OS << (getKillDest() ? "" : ", Merge");
-  OS << ")\n";
+  OS << "\n";
 }
 
 void OriginEscapesFact::dump(llvm::raw_ostream &OS, const LoanManager &,
@@ -53,7 +55,12 @@ void OriginEscapesFact::dump(llvm::raw_ostream &OS, const 
LoanManager &,
 void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
                    const OriginManager &OM) const {
   OS << "Use (";
-  OM.dump(getUsedOrigin(), OS);
+  size_t NumUsedOrigins = getUsedOrigins().size();
+  for (size_t I = 0; I < NumUsedOrigins; ++I) {
+    OM.dump(getUsedOrigins()[I], OS);
+    if (I < NumUsedOrigins - 1)
+      OS << ", ";
+  }
   OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
 }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 00870c3fd4086..3ac5005dc7897 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -6,25 +6,54 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#include <cassert>
+#include <string>
+
+#include "clang/AST/OperationKinds.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/Signals.h"
 #include "llvm/Support/TimeProfiler.h"
 
 namespace clang::lifetimes::internal {
 using llvm::isa_and_present;
 
-static bool isPointerType(QualType QT) {
-  return QT->isPointerOrReferenceType() || isGslPointerType(QT);
+OriginTree *FactsGenerator::getTree(const ValueDecl &D) {
+  return FactMgr.getOriginMgr().getOrCreateTree(&D);
 }
-// Check if a type has an origin.
-static bool hasOrigin(const Expr *E) {
-  return E->isGLValue() || isPointerType(E->getType());
+OriginTree *FactsGenerator::getTree(const Expr &E) {
+  return FactMgr.getOriginMgr().getOrCreateTree(&E);
 }
 
-static bool hasOrigin(const VarDecl *VD) {
-  return isPointerType(VD->getType());
+/// Propagates origin information from Src to Dst through all levels of
+/// indirection, creating OriginFlowFacts at each level.
+///
+/// This function enforces a critical type-safety invariant: both trees must
+/// have the same shape (same depth/structure). This invariant ensures that
+/// origins flow only between compatible types during expression evaluation.
+///
+/// Examples:
+///   - `int* p = &x;` flows origins from `&x` (depth 1) to `p` (depth 1)
+///   - `int** pp = &p;` flows origins from `&p` (depth 2) to `pp` (depth 2)
+///     * Level 1: pp <- p's address
+///     * Level 2: (*pp) <- what p points to (i.e., &x)
+///   - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
+void FactsGenerator::flow(OriginTree *Dst, OriginTree *Src, bool Kill) {
+  if (!Dst)
+    return;
+  assert(Src &&
+         "Dst is non-null but Src is null. Trees must have the same shape");
+  assert(Dst->getDepth() == Src->getDepth() &&
+         "Trees must have the same shape");
+
+  while (Dst && Src) {
+    CurrentBlockFacts.push_back(
+        FactMgr.createFact<OriginFlowFact>(Dst->OID, Src->OID, Kill));
+    Dst = Dst->Pointee;
+    Src = Src->Pointee;
+  }
 }
 
 /// Creates a loan for the storage path of a given declaration reference.
@@ -64,29 +93,43 @@ void FactsGenerator::run() {
 void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
   for (const Decl *D : DS->decls())
     if (const auto *VD = dyn_cast<VarDecl>(D))
-      if (hasOrigin(VD))
-        if (const Expr *InitExpr = VD->getInit())
-          killAndFlowOrigin(*VD, *InitExpr);
+      if (const Expr *InitExpr = VD->getInit()) {
+        OriginTree *VDTree = getTree(*VD);
+        if (!VDTree)
+          continue;
+        OriginTree *InitTree = getTree(*InitExpr);
+        assert(InitTree && "VarDecl had origins but InitExpr did not");
+        // Special handling for rvalue references initialized with xvalues.
+        // For declarations like `Ranges&& r = std::move(ranges);`, the rvalue
+        // reference should directly refer to the object being moved from,
+        // rather than creating a new indirection level. We skip the outer
+        // reference level and flow the pointee origins directly.
+        if (VD->getType()->isRValueReferenceType() && InitExpr->isXValue()) {
+          flow(VDTree->Pointee, InitTree->Pointee, /*Kill=*/true);
+          continue;
+        }
+        flow(VDTree, InitTree, /*Kill=*/true);
+      }
 }
 
 void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  // Skip function references and PR values.
+  if (DRE->getFoundDecl()->isFunctionOrFunctionTemplate() || !DRE->isGLValue())
+    return;
   handleUse(DRE);
-  // For non-pointer/non-view types, a reference to the variable's storage
-  // is a borrow. We create a loan for it.
-  // For pointer/view types, we stick to the existing model for now and do
-  // not create an extra origin for the l-value expression itself.
-
-  // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
-  // not sufficient to model the different levels of indirection. The current
-  // single-origin model cannot distinguish between a loan to the variable's
-  // storage and a loan to what it points to. A multi-origin model would be
-  // required for this.
-  if (!isPointerType(DRE->getType())) {
-    if (const Loan *L = createLoan(FactMgr, DRE)) {
-      OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
-      CurrentBlockFacts.push_back(
-          FactMgr.createFact<IssueFact>(L->ID, ExprOID));
-    }
+  // For pointer/view types, handleUse tracks all levels of indirection through
+  // the OriginTree structure.
+  //
+  // For non-pointer/non-reference types (e.g., `int x`), taking the address
+  // creates a borrow of the variable's storage. We issue a loan for this case.
+  if (doesDeclHaveStorage(DRE->getDecl())) {
+    const Loan *L = createLoan(FactMgr, DRE);
+    assert(L);
+    OriginTree *Tree = getTree(*DRE);
+    assert(Tree &&
+           "gl-value DRE of non-pointer type should have an origin tree");
+    CurrentBlockFacts.push_back(
+        FactMgr.createFact<IssueFact>(L->ID, Tree->OID));
   }
 }
 
@@ -100,12 +143,14 @@ void FactsGenerator::VisitCXXConstructExpr(const 
CXXConstructExpr *CCE) {
 void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
   // Specifically for conversion operators,
   // like `std::string_view p = std::string{};`
-  if (isGslPointerType(MCE->getType()) &&
-      isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl())) {
+  if (isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl()) &&
+      isGslPointerType(MCE->getType()) &&
+      isGslOwnerType(MCE->getImplicitObjectArgument()->getType())) {
     // The argument is the implicit object itself.
     handleFunctionCall(MCE, MCE->getMethodDecl(),
                        {MCE->getImplicitObjectArgument()},
                        /*IsGslConstruction=*/true);
+    return;
   }
   if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
     // Construct the argument list, with the implicit 'this' object as the
@@ -127,24 +172,46 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
     const CXXNullPtrLiteralExpr *N) {
   /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
   /// pointers can use the same type of loan.
-  FactMgr.getOriginMgr().getOrCreate(*N);
+  getTree(*N);
 }
 
 void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  if (!hasOrigin(ICE))
+  OriginTree *Dest = getTree(*ICE);
+  if (!Dest)
+    return;
+  OriginTree *SrcTree = getTree(*ICE->getSubExpr());
+
+  if (ICE->getCastKind() == CK_LValueToRValue) {
+    // TODO: Decide what to do for x-values here.
+    if (!ICE->getSubExpr()->isLValue())
+      return;
+
+    assert(SrcTree && "LValue being cast to RValue has no origin tree");
+    // The result of an LValue-to-RValue cast on a reference-to-pointer like
+    // has the inner origin....
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/168344
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to