llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-temporal-safety Author: Zhijie Wang (aeft) <details> <summary>Changes</summary> This is the first step toward pointer-field sensitivity (#<!-- -->184344). - `hasOrigins` extension: lambda closure types whose fields have origins now participate in origin tracking. - `VisitLambdaExpr`: each lambda gets a single merged origin. - Lambda closure copy/move constructors now propagate origins. --- Full diff: https://github.com/llvm/llvm-project/pull/185216.diff 5 Files Affected: - (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+1) - (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+29) - (modified) clang/lib/Analysis/LifetimeSafety/Origins.cpp (+13-1) - (modified) clang/test/Sema/warn-lifetime-safety.cpp (+75) - (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+47) ``````````diff 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 `````````` </details> https://github.com/llvm/llvm-project/pull/185216 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
