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

Reply via email to