Author: Kashika Akhouri
Date: 2026-01-16T16:33:24Z
New Revision: 6362bc81a0a7269e062550f37070a0dcac3ad399

URL: 
https://github.com/llvm/llvm-project/commit/6362bc81a0a7269e062550f37070a0dcac3ad399
DIFF: 
https://github.com/llvm/llvm-project/commit/6362bc81a0a7269e062550f37070a0dcac3ad399.diff

LOG: [LifetimeSafety] Run analysis in post-order of CallGraph for better 
annotation propagation (#174178)

Add functionality to analyze functions in the post-order of the call
graph.

The PR includes the following changes:

1. **Call Graph Generation**: Uses `clang::CallGraph` and
`addToCallGraph` to generate a call graph.
2. **Topological Traversal**: Uses `llvm::post_order` to iterate through
the CallGraph.
3. **New Frontend Flag**: The post-order analysis is enabled via a new
frontend flag `-fexperimental-lifetime-safety-inference-post-order`

Example:

```css
#include <iostream>
#include <string>

  std::string_view f_1(std::string_view a);
  std::string_view f_2(std::string_view a);
  std::string_view f_f(std::string_view a);

  std::string_view f_f(std::string_view a) {
    std::string stack = "something on stack";
    std::string_view res = f_2(stack);
    return res;
  }

 std::string_view f_2(std::string_view a) {
    return f_1(a);
  }

 std::string_view f_1(std::string_view a) {
    return a;
  }
```

Ouput:

```
s.cpp:20:26: warning: parameter in intra-TU function should be marked 
[[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
   20 |     std::string_view f_1(std::string_view a)
      |                          ^~~~~~~~~~~~~~~~~~
      |                                             [[clang::lifetimebound]]
s.cpp:22:12: note: param returned here
   22 |     return a;
      |            ^
s.cpp:15:26: warning: parameter in intra-TU function should be marked 
[[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
   15 |     std::string_view f_2(std::string_view a)
      |                          ^~~~~~~~~~~~~~~~~~
      |                                             [[clang::lifetimebound]]
s.cpp:17:12: note: param returned here
   17 |     return f_1(a);
      |            ^~~~~~
s.cpp:11:32: warning: address of stack memory is returned later 
[-Wexperimental-lifetime-safety-permissive]
   11 |     std::string_view res = f_2(stack);
      |                                ^~~~~
s.cpp:12:12: note: returned here
   12 |     return res;
```

Fixes https://github.com/llvm/llvm-project/issues/172862

---------

Co-authored-by: Utkarsh Saxena <[email protected]>

Added: 
    

Modified: 
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Options/Options.td
    clang/lib/Analysis/LifetimeSafety/Checker.cpp
    clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
    clang/lib/Sema/AnalysisBasedWarnings.cpp
    clang/test/Sema/warn-lifetime-safety-suggestions.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 8cba1dbaee24e..36fec24638363 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -505,6 +505,9 @@ LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, 
"Experimental lifetime safety
 
 LANGOPT(EnableLifetimeSafetyInference, 1, 0, NotCompatible, "Experimental 
lifetime safety inference analysis for C++")
 
+// TODO: Remove flag and default to end-of-TU analysis for lifetime safety 
after performance validation.
+LANGOPT(EnableLifetimeSafetyTUAnalysis, 1, 0, NotCompatible, "Experimental 
lifetime safety at translation-unit end, analyzing functions in call graph 
post-order for C++")
+
 LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector 
type")
 
 #undef LANGOPT

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index bffc111d1cf35..188739e72434a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1976,6 +1976,16 @@ defm lifetime_safety_inference
                   BothFlags<[], [CC1Option],
                             " experimental lifetime safety inference for 
C++">>;
 
+defm lifetime_safety_tu_analysis
+    : BoolFOption<"experimental-lifetime-safety-tu-analysis",
+                  LangOpts<"EnableLifetimeSafetyTUAnalysis">, DefaultFalse,
+                  PosFlag<SetTrue, [], [CC1Option], "Enable">,
+                  NegFlag<SetFalse, [], [CC1Option], "Disable">,
+                  BothFlags<[], [CC1Option],
+                            " run lifetime safety analysis at translation-unit 
"
+                            "end, analyzing functions in call graph post-order 
"
+                            "to best propagate inferred annotations">>;
+
 defm addrsig : BoolFOption<"addrsig",
   CodeGenOpts<"Addrsig">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Emit">,

diff  --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index ff2650be594f5..f7383126fac38 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
 #include "clang/AST/Expr.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
@@ -194,15 +195,18 @@ class LifetimeChecker {
   }
 
   void inferAnnotations() {
-    // FIXME: To maximise inference propagation, functions should be analyzed 
in
-    // post-order of the call graph, allowing inferred annotations to propagate
-    // through the call chain
-    // FIXME: Add the inferred attribute to all redeclarations of the function,
-    // not just the definition being analyzed.
     for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) {
       ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD);
-      if (!PVD->hasAttr<LifetimeBoundAttr>())
-        PVD->addAttr(
+      const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
+      if (!FD)
+        continue;
+      // Propagates inferred attributes via the most recent declaration to
+      // ensure visibility for callers in post-order analysis.
+      FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+      ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
+          FD->getParamDecl(PVD->getFunctionScopeIndex()));
+      if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
+        InferredPVD->addAttr(
             LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
     }
   }

diff  --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index bb82f09fa8457..24e5479923946 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -472,6 +472,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
                                         bool IsGslConstruction) {
   OriginList *CallList = getOriginsList(*Call);
   // Ignore functions returning values with no origin.
+  FD = getDeclWithMergedLifetimeBoundAttrs(FD);
   if (!FD || !CallList)
     return;
   auto IsArgLifetimeBound = [FD](unsigned I) -> bool {

diff  --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 7b08648080710..56d7db649afbe 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -36,6 +36,7 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CallGraph.h"
 #include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -48,10 +49,12 @@
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/TimeProfiler.h"
 #include <algorithm>
 #include <deque>
 #include <iterator>
@@ -2915,6 +2918,30 @@ class LifetimeSafetyReporterImpl : public 
LifetimeSafetyReporter {
 } // namespace
 } // namespace clang::lifetimes
 
+static void
+LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU,
+                         clang::lifetimes::LifetimeSafetyStats &LSStats) {
+  llvm::TimeTraceScope TimeProfile("LifetimeSafetyTUAnalysis");
+  CallGraph CG;
+  CG.addToCallGraph(TU);
+  lifetimes::LifetimeSafetyReporterImpl Reporter(S);
+  for (auto *Node : llvm::post_order(&CG)) {
+    const clang::FunctionDecl *CanonicalFD =
+        dyn_cast_or_null<clang::FunctionDecl>(Node->getDecl());
+    if (!CanonicalFD)
+      continue;
+    const FunctionDecl *FD = CanonicalFD->getDefinition();
+    if (!FD)
+      continue;
+    AnalysisDeclContext AC(nullptr, FD);
+    AC.getCFGBuildOptions().PruneTriviallyFalseEdges = false;
+    AC.getCFGBuildOptions().AddLifetime = true;
+    AC.getCFGBuildOptions().setAllAlwaysAdd();
+    if (AC.getCFG())
+      runLifetimeSafetyAnalysis(AC, &Reporter, LSStats, S.CollectStats);
+  }
+}
+
 void clang::sema::AnalysisBasedWarnings::IssueWarnings(
      TranslationUnitDecl *TU) {
   if (!TU)
@@ -2969,6 +2996,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
     CallableVisitor(CallAnalyzers, TU->getOwningModule())
         .TraverseTranslationUnitDecl(TU);
   }
+
+  if (S.getLangOpts().EnableLifetimeSafety && S.getLangOpts().CPlusPlus &&
+      S.getLangOpts().EnableLifetimeSafetyTUAnalysis)
+    LifetimeSafetyTUAnalysis(S, TU, LSStats);
 }
 
 void clang::sema::AnalysisBasedWarnings::IssueWarnings(
@@ -3015,7 +3046,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   AC.getCFGBuildOptions().AddCXXNewAllocator = false;
   AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
 
-  bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety;
+  bool EnableLifetimeSafetyAnalysis =
+      S.getLangOpts().EnableLifetimeSafety &&
+      !S.getLangOpts().EnableLifetimeSafetyTUAnalysis;
 
   if (EnableLifetimeSafetyAnalysis)
     AC.getCFGBuildOptions().AddLifetime = true;

diff  --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 68107b45c34b1..6e3a6f1fd9117 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -1,6 +1,6 @@
 // RUN: rm -rf %t
 // RUN: split-file %s %t
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety 
-fexperimental-lifetime-safety-inference 
-Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety 
-Wno-dangling -I%t -verify %t/test_source.cpp
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety 
-fexperimental-lifetime-safety-inference 
-fexperimental-lifetime-safety-tu-analysis 
-Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety 
-Wno-dangling -I%t -verify %t/test_source.cpp
 
 View definition_before_header(View a);
 
@@ -207,9 +207,8 @@ MyObj* return_pointer_by_func(MyObj* a) {         // 
expected-warning {{paramete
 namespace incorrect_order_inference_view {
 View return_view_callee(View a);
 
-// FIXME: No lifetime annotation suggestion when functions are not present in 
the callee-before-caller pattern
-View return_view_caller(View a) {
-  return return_view_callee(a);
+View return_view_caller(View a) {     // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
+  return return_view_callee(a);       // expected-note {{param returned here}}
 }
 
 View return_view_callee(View a) {     // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
@@ -220,9 +219,8 @@ View return_view_callee(View a) {     // expected-warning 
{{parameter in intra-T
 namespace incorrect_order_inference_object {
 MyObj* return_object_callee(MyObj* a);
 
-// FIXME: No lifetime annotation suggestion warning when functions are not 
present in the callee-before-caller pattern
-MyObj* return_object_caller(MyObj* a) {
-  return return_object_callee(a);
+MyObj* return_object_caller(MyObj* a) {      // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}.
+  return return_object_callee(a);            // expected-note {{param returned 
here}}
 }
 
 MyObj* return_object_callee(MyObj* a) {      // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}.
@@ -271,14 +269,14 @@ T* template_identity(T* a) {            // 
expected-warning {{parameter in intra
 }
 
 template<typename T>
-T* template_caller(T* a) {
-  return template_identity(a);          // expected-note {{in instantiation of 
function template specialization 
'inference_with_templates::template_identity<MyObj>' requested here}}
+T* template_caller(T* a) {              // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
+  return template_identity(a);          // expected-note {{param returned 
here}}
 }
 
-// FIXME: Fails to detect UAR as template instantiations are deferred to the 
end of the Translation Unit.
 MyObj* test_template_inference_with_stack() {
   MyObj local_stack;
-  return template_caller(&local_stack); // expected-note {{in instantiation of 
function template specialization 
'inference_with_templates::template_caller<MyObj>' requested here}}             
                                 
+  return template_caller(&local_stack);   // expected-warning {{address of 
stack memory is returned later}}
+                                          // expected-note@-1 {{returned 
here}}                                       
 }
 } // namespace inference_with_templates
 


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

Reply via email to