[llvm-branch-commits] [clang] [LifetimeSafety][NFC] Collect accessed fields in a unified pre-scan (PR #201511)

Zhijie Wang via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jun 11 00:01:34 PDT 2026


https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/201511

>From a7a42ac6e97bfc57f1c45e07d2e5891b5623b442 Mon Sep 17 00:00:00 2001
From: Zhijie Wang <yesterda9 at gmail.com>
Date: Wed, 3 Jun 2026 20:58:24 -0700
Subject: [PATCH] [LifetimeSafety][NFC] Collect accessed fields in a unified
 pre-scan

---
 .../Analyses/LifetimeSafety/Origins.h         | 19 +++++++++---
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 30 ++++++++++++++-----
 2 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 816228532b31e..823ddbb6fd6ae 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -21,6 +21,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang::lifetimes::internal {
@@ -196,6 +197,10 @@ class OriginManager {
   bool hasOrigins(QualType QT) const;
   bool hasOrigins(const Expr *E) const;
 
+  bool isAccessedField(const FieldDecl *FD) const {
+    return AccessedFields.contains(FD);
+  }
+
   void dump(OriginID OID, llvm::raw_ostream &OS,
             const FieldDecl *FD = nullptr) const;
 
@@ -215,10 +220,13 @@ class OriginManager {
 
   void initializeThisOrigins(const Decl *D);
 
-  /// Pre-scans the function body (and constructor init lists) to discover
-  /// return types of lifetime-annotated calls (currently
-  /// [[clang::lifetimebound]]), registering them for origin tracking.
-  void collectLifetimeAnnotatedOriginTypes(const AnalysisDeclContext &AC);
+  /// Pre-scans the function body (and constructor init lists) to discover:
+  ///
+  /// 1. Return types of lifetime-annotated calls (currently
+  ///    [[clang::lifetimebound]]), registering them for origin tracking.
+  ///
+  /// 2. The fields it accesses; the rest are excluded from origin tracking.
+  void runPreScan(const AnalysisDeclContext &AC);
   void registerLifetimeAnnotatedOriginType(QualType QT);
 
   ASTContext &AST;
@@ -234,6 +242,9 @@ class OriginManager {
   /// because of lifetime annotations (currently [[clang::lifetimebound]]) on
   /// functions that return them.
   llvm::DenseSet<const Type *> LifetimeAnnotatedOriginTypes;
+  /// Fields accessed in the function body (or constructor init lists).
+  /// Fields outside this set are excluded from origin tracking.
+  llvm::SmallPtrSet<const FieldDecl *, 8> AccessedFields;
 };
 } // namespace clang::lifetimes::internal
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index c4cd935bfcb6d..510232f5dc983 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -51,8 +51,7 @@ class MissingOriginCollector
   LifetimeSafetyStats &LSStats;
 };
 
-class LifetimeAnnotatedOriginTypeCollector
-    : public RecursiveASTVisitor<LifetimeAnnotatedOriginTypeCollector> {
+class PreScanCollector : public RecursiveASTVisitor<PreScanCollector> {
 public:
   bool VisitCallExpr(const CallExpr *CE) {
     // Indirect calls (e.g., function pointers) are skipped because lifetime
@@ -67,6 +66,12 @@ class LifetimeAnnotatedOriginTypeCollector
     return true;
   }
 
+  bool VisitMemberExpr(const MemberExpr *ME) {
+    if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
+      AccessedFields.insert(FD);
+    return true;
+  }
+
   bool shouldVisitLambdaBody() const { return false; }
   bool shouldVisitTemplateInstantiations() const { return true; }
 
@@ -74,8 +79,13 @@ class LifetimeAnnotatedOriginTypeCollector
     return CollectedTypes;
   }
 
+  const llvm::SmallPtrSet<const FieldDecl *, 8> &getAccessedFields() const {
+    return AccessedFields;
+  }
+
 private:
   llvm::SmallVector<QualType> CollectedTypes;
+  llvm::SmallPtrSet<const FieldDecl *, 8> AccessedFields;
 
   void collect(const FunctionDecl *FD, QualType RetType) {
     if (!FD)
@@ -159,7 +169,7 @@ bool doesDeclHaveStorage(const ValueDecl *D) {
 
 OriginManager::OriginManager(const AnalysisDeclContext &AC)
     : AST(AC.getASTContext()) {
-  collectLifetimeAnnotatedOriginTypes(AC);
+  runPreScan(AC);
   initializeThisOrigins(AC.getDecl());
 }
 
@@ -315,16 +325,20 @@ void OriginManager::collectMissingOrigins(Stmt &FunctionBody,
   Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody));
 }
 
-void OriginManager::collectLifetimeAnnotatedOriginTypes(
-    const AnalysisDeclContext &AC) {
-  LifetimeAnnotatedOriginTypeCollector Collector;
+void OriginManager::runPreScan(const AnalysisDeclContext &AC) {
+  PreScanCollector Collector;
   if (Stmt *Body = AC.getBody())
     Collector.TraverseStmt(Body);
   if (const auto *CD = dyn_cast<CXXConstructorDecl>(AC.getDecl()))
-    for (const auto *Init : CD->inits())
-      Collector.TraverseStmt(Init->getInit());
+    for (const auto *Init : CD->inits()) {
+      if (const FieldDecl *FD = Init->getAnyMember())
+        AccessedFields.insert(FD);
+      if (Expr *InitE = Init->getInit())
+        Collector.TraverseStmt(InitE);
+    }
   for (QualType QT : Collector.getCollectedTypes())
     registerLifetimeAnnotatedOriginType(QT);
+  AccessedFields.insert_range(Collector.getAccessedFields());
 }
 
 void OriginManager::registerLifetimeAnnotatedOriginType(QualType QT) {



More information about the llvm-branch-commits mailing list