[clang] c11a099 - [LifetimeSafety] Propagate loans through the comma operator (#204379)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 17 12:56:44 PDT 2026
Author: Gábor Horváth
Date: 2026-06-17T20:56:39+01:00
New Revision: c11a0993281f0e14ef0ce87e391f66bf7b335488
URL: https://github.com/llvm/llvm-project/commit/c11a0993281f0e14ef0ce87e391f66bf7b335488
DIFF: https://github.com/llvm/llvm-project/commit/c11a0993281f0e14ef0ce87e391f66bf7b335488.diff
LOG: [LifetimeSafety] Propagate loans through the comma operator (#204379)
VisitBinaryOperator had no comma case, so a comma expression carried
none of its right operand's loans and a borrow used via a comma result
(e.g. `g = (f(), p)`) was silently dropped. Flow the RHS's origin into
the result.
Assisted-by: Claude Opus 4.8
Added:
Modified:
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
clang/test/Sema/LifetimeSafety/dangling-global.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 b45987cbc097c..d56703a4b29c4 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -468,6 +468,10 @@ void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
}
void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Comma) {
+ killAndFlowOrigin(*BO, *BO->getRHS());
+ return;
+ }
if (BO->isCompoundAssignmentOp())
return;
if (BO->getType()->isPointerType() && BO->isAdditiveOp())
diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
index 98f8905d0e2da..f419ff4416023 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 {{this global dangles}}
+int *global; // expected-note 2 {{this global dangles}}
int *global_backup; // expected-note {{this global dangles}}
struct ObjWithStaticField {
@@ -32,6 +32,12 @@ void store_local_in_global() {
global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}}
}
+int side();
+void store_local_in_global_via_comma() {
+ int local;
+ global = (side(), &local); // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global' which will dangle}}
+}
+
void store_then_clear() {
int local;
global = &local;
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 3c954cebda820..6fc275b51a9d0 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1250,6 +1250,43 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
(void)*p; // expected-note 4 {{later used here}}
}
+// Comma operator.
+int side();
+void comma_use_after_scope() {
+ MyObj* p;
+ {
+ MyObj temp;
+ p = (side(), &temp); // expected-warning {{local variable 'temp' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void comma_nested() {
+ MyObj* p;
+ {
+ MyObj temp;
+ p = (side(), (side(), &temp)); // expected-warning {{local variable 'temp' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void comma_masked_by_conditional(bool cond) {
+ MyObj safe;
+ MyObj* keep = &safe;
+ MyObj* p;
+ {
+ MyObj temp;
+ p = cond ? keep : (side(), &temp); // expected-warning {{local variable 'temp' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void comma_safe() {
+ MyObj safe;
+ MyObj* p = (side(), &safe);
+ (void)*p; // no-warning
+}
+
// 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() {
More information about the cfe-commits
mailing list