https://github.com/Xazax-hun updated https://github.com/llvm/llvm-project/pull/204477
From 98ad02b3e14b499d522a2f2b0e5db59bae04921f Mon Sep 17 00:00:00 2001 From: Gabor Horvath <[email protected]> Date: Wed, 17 Jun 2026 23:19:27 +0100 Subject: [PATCH] [LifetimeSafety] Propagate loans through pointer inc/dec and compound assignment VisitUnaryOperator modeled only address-of/deref, and VisitBinaryOperator early-returned for compound assignments, so a borrow used via the result of `++p`/`p++`/`p += n` etc. was silently dropped (e.g. `global = ++p;` missed -Wlifetime-safety-dangling-global). These keep the pointer in the same allocation, so flow the operand's loans into the result, peeling the storage origin when the result is a prvalue (post-inc/dec, or any form in C). Assisted-by: Claude Opus 4.8 --- .../LifetimeSafety/FactsGenerator.cpp | 27 +++++++++- .../Sema/LifetimeSafety/dangling-global.cpp | 49 ++++++++++++++++++- clang/test/Sema/LifetimeSafety/safety-c.c | 22 +++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index d56703a4b29c4..d769fd8e5d56a 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -372,6 +372,21 @@ void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) { killAndFlowOrigin(*UO, *SubExpr); return; } + case UO_PreInc: + case UO_PostInc: + case UO_PreDec: + case UO_PostDec: { + // Incrementing/decrementing a pointer keeps it in the same allocation, so + // the result carries the operand's loans. The operand is always an lvalue; + // peel its storage origin when the result is a prvalue (post-inc/dec, or + // any form in C). + if (!UO->getType()->isPointerType()) + return; + OriginList *SubList = getOriginsList(*UO->getSubExpr()); + flow(getOriginsList(*UO), + UO->isGLValue() ? SubList : SubList->peelOuterOrigin(), /*Kill=*/true); + return; + } default: return; } @@ -472,8 +487,18 @@ void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) { killAndFlowOrigin(*BO, *BO->getRHS()); return; } - if (BO->isCompoundAssignmentOp()) + if (BO->isCompoundAssignmentOp()) { + // A pointer compound additive assignment (`p += n`) keeps the pointer in + // the same allocation, so its result carries the LHS pointer's loans. In C + // the result is a prvalue, so peel the LHS's storage origin. + if (BO->getType()->isPointerType()) { + OriginList *LHSList = getOriginsList(*BO->getLHS()); + flow(getOriginsList(*BO), + IsCMode ? getRValueOrigins(BO->getLHS(), LHSList) : LHSList, + /*Kill=*/true); + } return; + } if (BO->getType()->isPointerType() && BO->isAdditiveOp()) handlePointerArithmetic(BO); handleUse(BO->getRHS()); diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp index f419ff4416023..d2636f779ef74 100644 --- a/clang/test/Sema/LifetimeSafety/dangling-global.cpp +++ b/clang/test/Sema/LifetimeSafety/dangling-global.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify %s -int *global; // expected-note 2 {{this global dangles}} +int *global; // expected-note 8 {{this global dangles}} int *global_backup; // expected-note {{this global dangles}} struct ObjWithStaticField { @@ -48,3 +48,50 @@ void dangling_static_field() { int local; ObjWithStaticField::static_field = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the static variable 'static_field' which will dangle}} } + +// Pointer compound assignment and increment/decrement keep the pointer in the +// same allocation, so the result carries the borrow. +void via_compound_add() { + int local[10]; + int *p = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = (p += 1); +} + +void via_compound_sub() { + int local[10]; + int *p = local + 5; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = (p -= 1); +} + +void via_preinc() { + int local[10]; + int *p = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = ++p; +} + +void via_postinc() { + int local[10]; + int *p = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = p++; +} + +void via_predec() { + int local[10]; + int *p = local + 5; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = --p; +} + +void via_postdec() { + int local[10]; + int *p = local + 5; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}} + global = p--; +} + +// Negative: arithmetic on a pointer into long-lived storage stays silent. +void ok_global_storage() { + static int s[10]; + int *p = s; + p += 1; + ++p; + global = (p -= 1); // no-warning +} diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c index 95c8cf7bb00c7..13b92a8d81db4 100644 --- a/clang/test/Sema/LifetimeSafety/safety-c.c +++ b/clang/test/Sema/LifetimeSafety/safety-c.c @@ -179,3 +179,25 @@ int *atomic_pointer_declref(void) { _Atomic(int *) p = &value; return p; } + +// In C, a pointer compound assignment is a prvalue; its result still carries +// the LHS pointer's loans. +void compound_assign_prvalue(void) { + int *p; + { + int local[10]; + int *q = local; // expected-warning {{local variable 'local' does not live long enough}} + p = (q += 1); + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void preincrement_prvalue(void) { + int *p; + { + int local[10]; + int *q = local; // expected-warning {{local variable 'local' does not live long enough}} + p = ++q; + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
