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 λ // 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 λ // 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
