Author: Gábor Horváth Date: 2026-07-03T08:58:44+01:00 New Revision: 448e7fec76bdedb50c2e8e17f68440b505da369c
URL: https://github.com/llvm/llvm-project/commit/448e7fec76bdedb50c2e8e17f68440b505da369c DIFF: https://github.com/llvm/llvm-project/commit/448e7fec76bdedb50c2e8e17f68440b505da369c.diff LOG: [LifetimeSafety] Track unary plus on a pointer (#207243) Unary plus on a pointer is the identity (+p == p), so the result carries the operand's loans -- but UO_Plus fell through VisitUnaryOperator's default and left the result origin empty, dropping the borrow (e.g. p = +&local was a silently-missed use-after-scope). Handle it by flowing the operand's rvalue origins. Assisted-by: Claude Opus 4.8 Co-authored-by: Gabor Horvath <[email protected]> Added: Modified: clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp clang/test/Sema/LifetimeSafety/safety.cpp Removed: ################################################################################ diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b909b8b568aee..095bee8135c20 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -399,6 +399,17 @@ void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) { killAndFlowOrigin(*UO, *SubExpr); return; } + case UO_Plus: { + // Unary plus on a pointer is the identity (`+p == p`), so the prvalue + // result carries the operand's loans. Flow the operand's rvalue origins + // (peeling storage only when the operand is itself a glvalue). + if (!UO->getType()->isPointerType()) + return; + const Expr *SubExpr = UO->getSubExpr(); + flow(getOriginsList(*UO), + getRValueOrigins(SubExpr, getOriginsList(*SubExpr)), /*Kill=*/true); + return; + } case UO_PreInc: case UO_PostInc: case UO_PreDec: diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index 57369a95d0e82..7362ca632b2e5 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -1370,6 +1370,53 @@ FalseView binary_conditional_folded_false(FalseView fb) { return FalseView(local) ?: fb; // no-warning (result is always fb) } +// Unary plus on a pointer is the identity, so the result carries the operand's +// loans. +namespace unary_plus { +void use(int *p); + +void borrow_of_local() { + int *p; + { + int local = 0; + p = +&local; // expected-warning {{local variable 'local' does not live long enough}} + } // expected-note {{local variable 'local' is destroyed here}} + use(p); // expected-note {{later used here}} +} + +int *return_borrow_of_local() { + int local = 0; + return +&local; // expected-warning {{stack memory associated with local variable 'local' is returned}} + // expected-note@-1 {{returned here}} +} + +// A pointer glvalue operand forwards its loan too. +void forward_pointer_value() { + int *p; + { + int local = 0; + int *q = &local; // expected-warning {{local variable 'local' does not live long enough}} + p = +q; // expected-note {{local variable 'q' aliases the storage of local variable 'local'}} + } // expected-note {{local variable 'local' is destroyed here}} + use(p); // expected-note {{later used here}} +} + +// Negative: a long-lived borrow stays silent. +void ok() { + static int s; + int *p = +&s; + use(p); // no-warning +} + +// Multi-level: the deeper origin flows through unary plus too. +int **return_multilevel() { + int a = 1; + int *b = &a; // expected-warning {{stack memory associated with local variable 'a' is returned}} + int **c = +&b; // expected-warning {{stack memory associated with local variable 'b' is returned}} + return c; // expected-note 2 {{returned here}} +} +} // namespace unary_plus + // FIXME: Diagnostic output does not handle ParenExpr correctly, causing alias // information to be missed (local variable 'p' aliases the storage of local variable 'b'). void simpleparen() { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
