Author: Zhijie Wang Date: 2026-03-18T11:16:18+01:00 New Revision: 8c4f4e8a05abb18ba803a2425c274874a2c401b8
URL: https://github.com/llvm/llvm-project/commit/8c4f4e8a05abb18ba803a2425c274874a2c401b8 DIFF: https://github.com/llvm/llvm-project/commit/8c4f4e8a05abb18ba803a2425c274874a2c401b8.diff LOG: [LifetimeSafety] Track origins through array subscript and array-to-pointer decay (#186902) Array element accesses and array-to-pointer decay were not tracked because `CK_ArrayToPointerDecay` dropped origins and `ArraySubscriptExpr` had no visitor. This patch adds both to propagate origins through array operations. Fixes #186075 Added: Modified: clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp clang/test/Sema/warn-lifetime-analysis-nocfg.cpp clang/test/Sema/warn-lifetime-safety-suggestions.cpp clang/test/Sema/warn-lifetime-safety.cpp Removed: ################################################################################ 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 4996664d26452..a725119444e2f 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..22c4222022ebf 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -423,3 +423,19 @@ 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}} + } + + 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 7fec336fad8be..bd09bb70e9a11 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1980,3 +1980,125 @@ 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* 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}} +} + +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: 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. +} + +// 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
