[clang] [LifetimeSafety] Add support for conditional operators (PR #167240)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Nov 9 11:26:14 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-temporal-safety
@llvm/pr-subscribers-clang-analysis
Author: Utkarsh Saxena (usx95)
<details>
<summary>Changes</summary>
Added support for conditional operators in the lifetime safety analysis.
Added a `VisitConditionalOperator` method to the `FactsGenerator` class to handle the ternary operator (`?:`) in lifetime safety analysis.
Fixes https://github.com/llvm/llvm-project/issues/157108
---
Full diff: https://github.com/llvm/llvm-project/pull/167240.diff
4 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+1)
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+7)
- (modified) clang/test/Sema/warn-lifetime-safety-dataflow.cpp (+19)
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+53)
``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 5e58abee2bbb3..4c8ab3f859a49 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -43,6 +43,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void VisitUnaryOperator(const UnaryOperator *UO);
void VisitReturnStmt(const ReturnStmt *RS);
void VisitBinaryOperator(const BinaryOperator *BO);
+ void VisitConditionalOperator(const ConditionalOperator *CO);
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE);
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE);
void VisitInitListExpr(const InitListExpr *ILE);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index bec8e1dabb0b5..fbb993f5ed6b2 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -176,6 +176,13 @@ void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
handleAssignment(BO->getLHS(), BO->getRHS());
}
+void FactsGenerator::VisitConditionalOperator(const ConditionalOperator *CO) {
+ if (hasOrigin(CO)) {
+ killAndFlowOrigin(*CO, *CO->getTrueExpr());
+ flowOrigin(*CO, *CO->getFalseExpr());
+ }
+}
+
void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
// Assignment operators have special "kill-then-propagate" semantics
// and are handled separately.
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 31148b990d6bd..1568289d148db 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -414,3 +414,22 @@ void test_use_lifetimebound_call() {
// CHECK: Expire ([[L_Y]] (Path: y))
// CHECK: Expire ([[L_X]] (Path: x))
}
+// CHECK-LABEL: Function: test_conditional_operator
+void test_conditional_operator(bool cond) {
+ MyObj x, y;
+ MyObj *p = cond ? &x : &y;
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_COND:[0-9]+]] (Path: cond), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr))
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: OriginFlow (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: OriginFlow (Dest: [[O_COND_OP:[0-9]+]] (Expr: ConditionalOperator), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
+// CHECK: OriginFlow (Dest: [[O_COND_OP]] (Expr: ConditionalOperator), Src: [[O_ADDR_Y]] (Expr: UnaryOperator), Merge)
+// CHECK: OriginFlow (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_COND_OP]] (Expr: ConditionalOperator))
+// CHECK: Expire ([[L_Y]] (Path: y))
+// CHECK: Expire ([[L_X]] (Path: x))
+}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 4f234f0ac6e2d..3057ac9385736 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -440,6 +440,7 @@ void no_error_loan_from_current_iteration(bool cond) {
//===----------------------------------------------------------------------===//
View Identity(View v [[clang::lifetimebound]]);
+MyObj* Identity(MyObj* v [[clang::lifetimebound]]);
View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);
MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]);
@@ -582,3 +583,55 @@ void lifetimebound_ctor() {
}
(void)v;
}
+
+// Conditional operator.
+void conditional_operator(bool cond) {
+ MyObj safe;
+ MyObj* p = &safe;
+ {
+ MyObj temp;
+ p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}}
+ : &safe;
+ } // expected-note {{destroyed here}}
+ if (cond) p = &safe;
+ (void)*p; // expected-note {{later used here}}
+
+ {
+ MyObj a, b;
+ p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
+ : &b; // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+
+ {
+ MyObj a, b, c, d;
+ p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}.
+ : &b // expected-warning {{object whose reference is captured does not live long enough}}.
+ : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}.
+ : &d; // expected-warning {{object whose reference is captured does not live long enough}}.
+ } // expected-note 4 {{destroyed here}}
+ (void)*p; // expected-note 4 {{later used here}}
+
+ {
+ MyObj a, b;
+ p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
+ : &b); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+
+ {
+ MyObj a, b;
+ p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}}
+ : Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+
+ {
+ MyObj a, b, c, d;
+ p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
+ : &b) // expected-warning {{object whose reference is captured does not live long enough}}
+ : Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}
+ : &d)); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note 4 {{destroyed here}}
+ (void)*p; // expected-note 4 {{later used here}}
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/167240
More information about the cfe-commits
mailing list