https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/186902
>From 28a594841caba6bcc05cd1633b44575a59f0e21b Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Mon, 16 Mar 2026 15:10:38 -0700 Subject: [PATCH 1/2] [LifetimeSafety] Add origin tracking for array --- .../Analyses/LifetimeSafety/FactsGenerator.h | 1 + .../LifetimeSafety/FactsGenerator.cpp | 16 ++- .../Sema/warn-lifetime-analysis-nocfg.cpp | 5 +- .../Sema/warn-lifetime-safety-suggestions.cpp | 12 ++ clang/test/Sema/warn-lifetime-safety.cpp | 114 ++++++++++++++++++ 5 files changed, 145 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index 9bcf5193ef8fc..dfcbdc7d73007 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -51,6 +51,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE); void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE); void VisitLambdaExpr(const LambdaExpr *LE); + void VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE); private: OriginList *getOriginsList(const ValueDecl &D); diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 888176f09c9b9..3259505584c9f 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -296,9 +296,13 @@ void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { if (Dest && Src && Dest->getLength() == Src->getLength()) flow(Dest, Src, /*Kill=*/true); return; + case CK_ArrayToPointerDecay: + assert(Src && "Array expression should have origins as it is GL value"); + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + Dest->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true)); + return; case CK_FunctionToPointerDecay: case CK_BuiltinFnToFnPtr: - case CK_ArrayToPointerDecay: // Ignore function-to-pointer decays. return; default: @@ -470,6 +474,16 @@ void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) { } } +void FactsGenerator::VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) { + assert(ASE->isGLValue() && "Array subscript should be a GL value"); + OriginList *Dst = getOriginsList(*ASE); + assert(Dst && "Array subscript should have origins as it is a GL value"); + OriginList *Src = getOriginsList(*ASE->getBase()); + assert(Src && "Base of array subscript should have origins"); + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + Dst->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true)); +} + bool FactsGenerator::escapesViaReturn(OriginID OID) const { return llvm::any_of(EscapesInCurrentBlock, [OID](const Fact *F) { if (const auto *EF = F->getAs<ReturnEscapeFact>()) diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 3305e9e270d86..c4cc07e118763 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -77,8 +77,9 @@ struct Y { }; void dangligGslPtrFromTemporary() { - MyIntPointer p = Y{}.a; // TODO - (void)p; + MyIntPointer p = Y{}.a; // cfg-warning {{object whose reference is captured does not live long enough}} \ + // cfg-note {{destroyed here}} + (void)p; // cfg-note {{later used here}} } struct DanglingGslPtrField { diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index b7e6c5951ba8b..754088e2384c8 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -423,3 +423,15 @@ auto implicit_value_capture(int integer, return [=]() { Foo(integer, ptr, ref, view); }; // expected-note 2 {{param returned here}} } } // namespace lambda_captures + +namespace array { + +struct MemberArrayReturn { + int arr[10]; + + int* getFirst() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return &arr[0]; // expected-note {{param returned here}} + } +}; + +} // namespace array diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 7fec336fad8be..43781ccc01a85 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1980,3 +1980,117 @@ void outer_pointer_outlives_inner_pointee() { } } // namespace LoopLocalPointers + +namespace array { + +void element_use_after_scope() { + int* p; + { + int a[10]{}; + p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +int* element_use_after_return() { + int a[10]{}; + int* p = &a[0]; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} +} + +void element_use_same_scope() { + int a[10]{}; + int* p = &a[0]; + (void)*p; +} + +void element_reassigned_safe() { + int safe[10]{}; + int* p; + { + int a[10]{}; + p = &a[0]; + } + p = &safe[0]; // rescued + (void)*p; +} + +void multidimensional_use_after_scope() { + int* p; + { + int a[3][4]{}; + p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void member_array_element_use_after_scope() { + struct S { + int arr[10]; + int b; + }; + int* p; + { + S s; + p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void array_of_pointers_use_after_scope() { + int** p; + { + int x = 0; + int* a[10] = {&x}; + p = a; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void reversed_subscript_use_after_scope() { + int* p; + { + int a[10]{}; + p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +int* return_decayed_array() { + int a[10]{}; + int *p = a; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} +} + +int* param_array_element(int a[], int n) { + return &a[n]; +} + +int* static_array() { + static int a[10]{}; + return &a[1]; +} + +// FIXME: Pointer arithmetic is not yet tracked. +void pointer_arithmetic_use_after_scope() { + int* p; + { + int a[10]{}; + p = a + 5; + } + (void)*p; // should warn +} + +// FIXME: Reading a pointer value stored inside an array element +// is not yet tracked. +void read_pointer_from_array_element_use_after_scope() { + int* q; + { + int x = 0; + int* arr[10] = {&x}; + q = arr[0]; + } + (void)*q; // should warn +} + +} // namespace array >From 1f961acd9f688cd52655507a94d8de4db08f3cde Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Tue, 17 Mar 2026 16:08:01 -0700 Subject: [PATCH 2/2] address review comments --- .../Sema/warn-lifetime-safety-suggestions.cpp | 4 ++++ clang/test/Sema/warn-lifetime-safety.cpp | 24 ++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 754088e2384c8..22c4222022ebf 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -432,6 +432,10 @@ struct MemberArrayReturn { int* getFirst() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return &arr[0]; // expected-note {{param returned here}} } + + int* getData() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return arr; // expected-note {{param returned here}} + } }; } // namespace array diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 43781ccc01a85..bd09bb70e9a11 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2011,7 +2011,7 @@ void element_reassigned_safe() { int a[10]{}; p = &a[0]; } - p = &safe[0]; // rescued + p = &safe[0]; // Rescued. (void)*p; } @@ -2040,8 +2040,7 @@ void member_array_element_use_after_scope() { void array_of_pointers_use_after_scope() { int** p; { - int x = 0; - int* a[10] = {&x}; + int* a[10]{}; p = a; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} @@ -2078,19 +2077,28 @@ void pointer_arithmetic_use_after_scope() { int a[10]{}; p = a + 5; } - (void)*p; // should warn + (void)*p; // Should warn. } -// FIXME: Reading a pointer value stored inside an array element -// is not yet tracked. -void read_pointer_from_array_element_use_after_scope() { +// FIXME: Copying a pointer value out of an array element is not tracked. +void copy_pointer_from_array_use_after_scope() { int* q; { int x = 0; int* arr[10] = {&x}; q = arr[0]; } - (void)*q; // should warn + (void)*q; // Should warn. +} + +// FIXME: A pointer inside an array becoming dangling is not detected. +void pointer_in_array_use_after_scope() { + int* arr[10]; + { + int x = 0; + arr[0] = &x; + } + (void)*arr[0]; // Should warn. } } // namespace array _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
