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

>From ee2dcc8f33a2df8f7cd3e2bcafa28210e65aa157 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Sat, 7 Mar 2026 10:19:58 -0800
Subject: [PATCH 1/9] [LifetimeSafety] Add origin tracking for lambda captures

---
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  1 +
 .../LifetimeSafety/FactsGenerator.cpp         | 29 +++++++
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 14 +++-
 clang/test/Sema/warn-lifetime-safety.cpp      | 75 +++++++++++++++++++
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 47 ++++++++++++
 5 files changed, 165 insertions(+), 1 deletion(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index dbe5a1eeb498e..ddaa69719b666 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,6 +50,7 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   void VisitInitListExpr(const InitListExpr *ILE);
   void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE);
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
+  void VisitLambdaExpr(const LambdaExpr *LE);
 
 private:
   OriginList *getOriginsList(const ValueDecl &D);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index f39d677758393..61fd7359633d2 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -181,6 +181,14 @@ void FactsGenerator::VisitCXXConstructExpr(const 
CXXConstructExpr *CCE) {
     handleGSLPointerConstruction(CCE);
     return;
   }
+  if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
+      RD && RD->isLambda() && CCE->getNumArgs() == 1) {
+    const Expr *Arg = CCE->getArg(0);
+    if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
+      flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
+      return;
+    }
+  }
   handleFunctionCall(CCE, CCE->getConstructor(),
                      {CCE->getArgs(), CCE->getNumArgs()},
                      /*IsGslConstruction=*/false);
@@ -431,6 +439,27 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
   }
 }
 
+void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) {
+  // The lambda gets a single merged origin that aggregates all captured
+  // pointer-like origins. Currently we only need to detect whether the lambda
+  // outlives any capture.
+  OriginList *LambdaList = getOriginsList(*LE);
+  if (!LambdaList)
+    return;
+  bool Kill = true;
+  for (unsigned I = 0; I < LE->capture_size(); ++I) {
+    const Expr *Init = LE->capture_init_begin()[I];
+    if (!Init)
+      continue;
+    OriginList *InitList = getOriginsList(*Init);
+    if (!InitList)
+      continue;
+    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+        LambdaList->getOuterOriginID(), InitList->getOuterOriginID(), Kill));
+    Kill = false;
+  }
+}
+
 void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
   const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
   if (!LifetimeEndsVD)
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp 
b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index a9e40d6b7aaf1..0122f7a734541 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -51,7 +51,19 @@ class MissingOriginCollector
 } // namespace
 
 bool hasOrigins(QualType QT) {
-  return QT->isPointerOrReferenceType() || isGslPointerType(QT);
+  if (QT->isPointerOrReferenceType() || isGslPointerType(QT))
+    return true;
+  const auto *RD = QT->getAsCXXRecordDecl();
+  if (!RD)
+    return false;
+  // TODO: Limit to lambdas for now. This will be extended to user-defined
+  // structs with pointer-like fields.
+  if (!RD->isLambda())
+    return false;
+  for (const auto *FD : RD->fields())
+    if (hasOrigins(FD->getType()))
+      return true;
+  return false;
 }
 
 /// Determines if an expression has origins that need to be tracked.
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index a75c70aa3674a..dc7ce22913755 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1784,3 +1784,78 @@ void test_optional_view_arrow() {
     (void)*p;
 }
 } // namespace OwnerArrowOperator
+
+namespace lambda_captures {
+  auto return_ref_capture() {
+    int local = 1;
+    auto lambda = [&local]() { return local; }; // expected-warning {{address 
of stack memory is returned later}}
+    return lambda; // expected-note {{returned here}}
+  }
+
+  void safe_ref_capture() {
+    int local = 1;
+    auto lambda = [&local]() { return local; };
+    lambda();
+  }
+
+  auto capture_int_by_value() {
+    int x = 1;
+    auto lambda = [x]() { return x; };
+    return lambda;
+  }
+
+  auto capture_view_by_value() {
+    MyObj obj;
+    View v(obj); // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [v]() { return v; };
+    return lambda; // expected-note {{returned here}}
+  }
+
+  void capture_view_by_value_safe() {
+    MyObj obj;
+    View v(obj);
+    auto lambda = [v]() { return v; };
+    lambda();
+  }
+
+  auto capture_pointer_by_ref() {
+    MyObj obj;
+    MyObj* p = &obj;
+    auto lambda = [&p]() { return p; }; // expected-warning {{address of stack 
memory is returned later}}
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto capture_multiple() {
+    int a, b;
+    auto lambda = [&a, &b]() { return a + b; }; // expected-warning 2 
{{address of stack memory is returned later}}
+    return lambda; // expected-note 2 {{returned here}}
+  }
+
+  auto capture_raw_pointer_by_value() {
+    int x;
+    int* p = &x; // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [p]() { return p; };
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto capture_raw_pointer_init_capture() {
+    int x;
+    int* p = &x; // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [q = p]() { return q; };
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto capture_view_init_capture() {
+    MyObj obj;
+    View v(obj); // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [w = v]() { return w; };
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto capture_lambda() {
+    int x;
+    auto inner = [&x]() { return x; }; // expected-warning {{address of stack 
memory is returned later}}
+    auto outer = [inner]() { return inner(); };
+    return outer; // expected-note {{returned here}}
+  }
+} // namespace lambda_captures
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index a27f746fffb60..2116f7736c4be 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -1904,5 +1904,52 @@ TEST_F(LifetimeAnalysisTest, 
DerivedViewWithNoAnnotation) {
   // EXPECT_THAT(Origin("view"), HasLoansTo({"my_obj_or"}, "p1"));
 }
 
+TEST_F(LifetimeAnalysisTest, LambdaCaptureByRef) {
+  SetupTest(R"(
+    void target() {
+      int x;
+      int* p = &x;
+      auto lambda = [&p]() { return p; };
+      POINT(after_lambda);
+    }
+  )");
+  EXPECT_THAT(Origin("lambda"), HasLoansTo({"p"}, "after_lambda"));
+}
+
+TEST_F(LifetimeAnalysisTest, LambdaCaptureViewByValue) {
+  SetupTest(R"(
+    void target() {
+      MyObj obj;
+      View v(obj);
+      auto lambda = [v]() { return v; };
+      POINT(after_lambda);
+    }
+  )");
+  EXPECT_THAT(Origin("lambda"), HasLoansTo({"obj"}, "after_lambda"));
+}
+
+TEST_F(LifetimeAnalysisTest, LambdaInitCaptureRawPointerByValue) {
+  SetupTest(R"(
+    void target() {
+      int x;
+      int* p = &x;
+      auto lambda = [q = p]() { return q; };
+      POINT(after_lambda);
+    }
+  )");
+  EXPECT_THAT(Origin("lambda"), HasLoansTo({"x"}, "after_lambda"));
+}
+
+TEST_F(LifetimeAnalysisTest, LambdaInitCaptureViewByValue) {
+  SetupTest(R"(
+    void target() {
+      MyObj obj;
+      View v(obj);
+      auto lambda = [w = v]() { return w; };
+      POINT(after_lambda);
+    }
+  )");
+  EXPECT_THAT(Origin("lambda"), HasLoansTo({"obj"}, "after_lambda"));
+}
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

>From 1ada107c9c5b14cc8e20df78e610afbedc6054ce Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Mon, 9 Mar 2026 13:01:42 -0700
Subject: [PATCH 2/9] add comment for VisitCXXConstructExpr

---
 clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 8 +++++++-
 clang/test/Sema/warn-lifetime-safety.cpp             | 7 +++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 61fd7359633d2..9e3c5cce3e255 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -181,8 +181,14 @@ void FactsGenerator::VisitCXXConstructExpr(const 
CXXConstructExpr *CCE) {
     handleGSLPointerConstruction(CCE);
     return;
   }
+  // Implicit copy/move constructors of lambda closures lack
+  // [[clang::lifetimebound]], so `handleFunctionCall` cannot propagate 
origins.
+  // Handle them directly to keep the origin chain intact (e.g., `return
+  // lambda;` copies the closure).
   if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
-      RD && RD->isLambda() && CCE->getNumArgs() == 1) {
+      RD && RD->isLambda() &&
+      CCE->getConstructor()->isCopyOrMoveConstructor() &&
+      CCE->getNumArgs() == 1) {
     const Expr *Arg = CCE->getArg(0);
     if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
       flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index dc7ce22913755..fa43e25c2cef6 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1858,4 +1858,11 @@ namespace lambda_captures {
     auto outer = [inner]() { return inner(); };
     return outer; // expected-note {{returned here}}
   }
+
+  auto return_copied_lambda() {
+    int local = 1;
+    auto lambda = [&local]() { return local; }; // expected-warning {{address 
of stack memory is returned later}}
+    auto lambda_copy = lambda;
+    return lambda_copy; // expected-note {{returned here}}
+  }
 } // namespace lambda_captures

>From 99a6fba7873e8dd804f2531f92c2fb162c14b689 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Mon, 9 Mar 2026 13:11:54 -0700
Subject: [PATCH 3/9] add tests for lambda implict capture

---
 .../lib/Analysis/LifetimeSafety/FactsGenerator.cpp  |  3 +--
 clang/test/Sema/warn-lifetime-safety.cpp            | 13 +++++++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 9e3c5cce3e255..a16f92a3a6b9b 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -453,8 +453,7 @@ void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) {
   if (!LambdaList)
     return;
   bool Kill = true;
-  for (unsigned I = 0; I < LE->capture_size(); ++I) {
-    const Expr *Init = LE->capture_init_begin()[I];
+  for (const Expr *Init : LE->capture_inits()) {
     if (!Init)
       continue;
     OriginList *InitList = getOriginsList(*Init);
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index fa43e25c2cef6..b019d9be9a536 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1865,4 +1865,17 @@ namespace lambda_captures {
     auto lambda_copy = lambda;
     return lambda_copy; // expected-note {{returned here}}
   }
+
+  auto implicit_ref_capture() {
+    int local = 1, local2 = 2;
+    auto lambda = [&]() { return local; }; // expected-warning {{address of 
stack memory is returned later}}
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto implicit_value_capture() {
+    MyObj obj;
+    View v(obj); // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [=]() { return v; };
+    return lambda; // expected-note {{returned here}}
+  }
 } // namespace lambda_captures

>From 60589967136680408b2823db8cbbcc7693134ff4 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Mon, 9 Mar 2026 15:51:29 -0700
Subject: [PATCH 4/9] add TODO to improve diagnostic for implicit lambda
 captures

---
 clang/test/Sema/warn-lifetime-safety.cpp | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index b019d9be9a536..4a8112cb88ac3 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1827,7 +1827,10 @@ namespace lambda_captures {
 
   auto capture_multiple() {
     int a, b;
-    auto lambda = [&a, &b]() { return a + b; }; // expected-warning 2 
{{address of stack memory is returned later}}
+    auto lambda = [
+      &a,  // expected-warning {{address of stack memory is returned later}}
+      &b   // expected-warning {{address of stack memory is returned later}}
+    ]() { return a + b; };
     return lambda; // expected-note 2 {{returned here}}
   }
 
@@ -1872,6 +1875,15 @@ namespace lambda_captures {
     return lambda; // expected-note {{returned here}}
   }
 
+  // TODO: Include the name of the variable in the diagnostic to improve
+  // clarity, especially for implicit lambda captures where multiple warnings
+  // can point to the same source location.
+  auto implicit_ref_capture_multiple() {
+    int local = 1, local2 = 2;
+    auto lambda = [&]() { return local + local2; }; // expected-warning 2 
{{address of stack memory is returned later}}
+    return lambda; // expected-note 2 {{returned here}}
+  }
+
   auto implicit_value_capture() {
     MyObj obj;
     View v(obj); // expected-warning {{address of stack memory is returned 
later}}

>From ff7fa91df9ce901a643ba719885e1ca661152c17 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Mon, 9 Mar 2026 15:58:27 -0700
Subject: [PATCH 5/9] add test pointer_to_lambda_outlives

---
 clang/test/Sema/warn-lifetime-safety.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 4a8112cb88ac3..d1fe668695117 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1890,4 +1890,10 @@ namespace lambda_captures {
     auto lambda = [=]() { return v; };
     return lambda; // expected-note {{returned here}}
   }
+
+  auto* pointer_to_lambda_outlives() {
+    auto lambda = []() { return 42; };
+    return &lambda; // expected-warning {{address of stack memory is returned 
later}} \
+                    // expected-note {{returned here}}
+  }
 } // namespace lambda_captures

>From daf8c1c4b981ce7caecb8c8d5e4bd3c19a97e37f Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Tue, 10 Mar 2026 14:07:33 -0700
Subject: [PATCH 6/9] add tests

---
 .../Sema/warn-lifetime-safety-suggestions.cpp | 19 +++++++++++++++++++
 clang/test/Sema/warn-lifetime-safety.cpp      | 11 +++++++++++
 2 files changed, 30 insertions(+)

diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 4bd8a717d9d30..d574101f762fc 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -403,3 +403,22 @@ struct NoSuggestionForThisCapturedByLambda {
     };
   }
 };
+
+namespace lambda_captures {
+  void Foo(int, int*, const MyObj&, View);
+
+  auto implicit_ref_capture(int integer, int* ptr,
+                            const MyObj& ref, // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}
+                            View view) {
+    return [&]() { Foo(integer, ptr, ref, view); }; // expected-warning 3 
{{address of stack memory is returned later}} \
+                                                           // expected-note 3 
{{returned here}} \
+                                                           // expected-note 
{{param returned here}}
+  }
+
+  auto implicit_value_capture(int integer,
+                              int* ptr, // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}
+                              const MyObj& ref,
+                              View view) { // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}
+    return [=]() { Foo(integer, ptr, ref, view); }; // expected-note 2 {{param 
returned here}}
+  }
+} // namespace lambda_captures
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index d1fe668695117..6c2a08fb1d0ff 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1896,4 +1896,15 @@ namespace lambda_captures {
     return &lambda; // expected-warning {{address of stack memory is returned 
later}} \
                     // expected-note {{returned here}}
   }
+
+  auto capture_static() {
+    static int local = 1;
+    // Only automatic storage duration variables may be captured.
+    // Variables with static storage duration behave like globals and are 
directly accessible.
+    // The below lambdas should not capture `local`.
+    auto lambda = [&]() { return local; };
+    auto lambda2 = []() { return local; };
+    lambda2();
+    return lambda;
+  }
 } // namespace lambda_captures

>From d891015de2e2f0c07aec777b8c61776721413994 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Tue, 10 Mar 2026 14:20:49 -0700
Subject: [PATCH 7/9] add test: capture_static_address

---
 clang/test/Sema/warn-lifetime-safety.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 6c2a08fb1d0ff..07317a4e6c3a4 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1907,4 +1907,11 @@ namespace lambda_captures {
     lambda2();
     return lambda;
   }
+
+  auto capture_static_address() {
+      static int local = 1;
+      int* p = &local;
+      auto lambda = [p]() { return p; };
+      return lambda;
+  }
 } // namespace lambda_captures

>From 99abe37a88573d37a7bade875e3d542f7dcf9c3f Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Tue, 10 Mar 2026 15:18:01 -0700
Subject: [PATCH 8/9] add test: capture_static_address_by_ref

---
 clang/test/Sema/warn-lifetime-safety.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 07317a4e6c3a4..3639bce1a0364 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1908,10 +1908,17 @@ namespace lambda_captures {
     return lambda;
   }
 
-  auto capture_static_address() {
+  auto capture_static_address_by_value() {
       static int local = 1;
       int* p = &local;
       auto lambda = [p]() { return p; };
       return lambda;
   }
+
+  auto capture_static_address_by_ref() {
+      static int local = 1;
+      int* p = &local;
+      auto lambda = [&p]() { return p; }; // expected-warning {{address of 
stack memory is returned later}}
+      return lambda; // expected-note {{returned here}}
+  }
 } // namespace lambda_captures

>From 4b62f648f9dfd75102db1112f4bd20c39af07d5f Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Wed, 11 Mar 2026 15:52:45 -0700
Subject: [PATCH 9/9] add tests: lambda_capture_invalidation,
 capture_multilevel_pointer

---
 .../LifetimeSafety/FactsGenerator.cpp         |  4 ++
 clang/test/Sema/Inputs/lifetime-analysis.h    |  3 ++
 .../warn-lifetime-safety-invalidations.cpp    | 39 +++++++++++++++++++
 clang/test/Sema/warn-lifetime-safety.cpp      | 27 ++++++++-----
 4 files changed, 64 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index a16f92a3a6b9b..00df3b8007a89 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -459,6 +459,10 @@ void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) 
{
     OriginList *InitList = getOriginsList(*Init);
     if (!InitList)
       continue;
+    // FIXME: Consider flowing all origin levels once lambdas support more than
+    // one origin. Currently only the outermost origin is flowed, so by-ref
+    // captures like `[&p]` (where p is string_view) miss inner-level
+    // invalidation.
     CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
         LambdaList->getOuterOriginID(), InitList->getOuterOriginID(), Kill));
     Kill = false;
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h 
b/clang/test/Sema/Inputs/lifetime-analysis.h
index 85b5a5fe5e07f..56cacdd964f79 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -152,6 +152,7 @@ struct basic_string_view {
   basic_string_view(const T *);
   const T *begin() const;
   const T *data() const;
+  int size() const;
 };
 using string_view = basic_string_view<char>;
 
@@ -174,6 +175,8 @@ struct basic_string {
   basic_string& operator=(const basic_string&);
   basic_string& operator+=(const basic_string&);
   basic_string& operator+=(const T*);
+  void push_back(T);
+  void clear();
   const T *c_str() const;
   operator basic_string_view<T> () const;
   using const_iterator = iter<T>;
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp 
b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index c50c1e2d77d65..519a7c2986b12 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -468,3 +468,42 @@ void 
FlatMapSubscriptMultipleCallsInvalidate(std::flat_map<int, int> mp, int a,
 }
 
 } // namespace AssociativeContainers
+
+namespace lambda_capture_invalidation {
+  void captured_view_invalidated_by_owner() {
+    std::string s = "42";
+    std::string_view p = s; // expected-warning {{object whose reference is 
captured is later invalidated}}
+    auto lambda = [=]() { return p; };
+    s.push_back('c');  // expected-note {{invalidated here}}
+    lambda();  // expected-note {{later used here}}
+  }
+
+  void multiple_captures_one_invalidated() {
+    std::string s1 = "a", s2 = "b";
+    std::string_view p1 = s1, p2 = s2; // expected-warning {{object whose 
reference is captured is later invalidated}}
+    auto lambda = [=]() { return p1.size() + p2.size(); };
+    s1.clear();  // expected-note {{invalidated here}}
+    lambda();  // expected-note {{later used here}}
+  }
+
+  // FIXME: By-ref captures flow only the outermost origin, so
+  // invalidation of the captured view's pointee is not propagated.
+  void ref_capture_owner_invalidated() {
+    std::string s = "42";
+    std::string_view p = s;
+    auto lambda = [&]() { return p; };
+    s.push_back('c');  // invalidates p
+    lambda();  // should warn: use-after-invalidate
+  }
+
+  // FIXME: Once inner origins are tracked, this case must remain a no-warning.
+  // Reassigning `p` through the by-ref capture should invalidate the link to 
`s`.
+  void ref_capture_reassigned_to_safe() {
+    std::string s = "42", safe = "not modified";
+    std::string_view p = s;
+    auto lambda = [&]() { return p; };
+    p = safe;  // p now points to 'safe', not 's'
+    s.push_back('c');  // does not invalidate p anymore
+    lambda();  // should not warn
+  }
+} // namespace lambda_capture_invalidation
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 3639bce1a0364..6ae62f49a7b8c 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1870,7 +1870,7 @@ namespace lambda_captures {
   }
 
   auto implicit_ref_capture() {
-    int local = 1, local2 = 2;
+    int local = 1;
     auto lambda = [&]() { return local; }; // expected-warning {{address of 
stack memory is returned later}}
     return lambda; // expected-note {{returned here}}
   }
@@ -1909,16 +1909,25 @@ namespace lambda_captures {
   }
 
   auto capture_static_address_by_value() {
-      static int local = 1;
-      int* p = &local;
-      auto lambda = [p]() { return p; };
-      return lambda;
+    static int local = 1;
+    int* p = &local;
+    auto lambda = [p]() { return p; };
+    return lambda;
   }
 
   auto capture_static_address_by_ref() {
-      static int local = 1;
-      int* p = &local;
-      auto lambda = [&p]() { return p; }; // expected-warning {{address of 
stack memory is returned later}}
-      return lambda; // expected-note {{returned here}}
+    static int local = 1;
+    int* p = &local;
+    auto lambda = [&p]() { return p; }; // expected-warning {{address of stack 
memory is returned later}}
+    return lambda; // expected-note {{returned here}}
+  }
+
+  auto capture_multilevel_pointer() {
+    int x;
+    int *p = &x; // expected-warning {{address of stack memory is returned 
later}}
+    int **q = &p; // expected-warning {{address of stack memory is returned 
later}}
+    int ***r = &q; // expected-warning {{address of stack memory is returned 
later}}
+    auto lambda = [=]() { return *p + **q + ***r; };
+    return lambda; // expected-note 3 {{returned here}}
   }
 } // namespace lambda_captures

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

Reply via email to