[clang] 0278214 - [LifetimeSafety] Fix crash on ternary operator when one of the expressions is `throw` (#190345)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 09:49:12 PDT 2026
Author: NeKon69
Date: 2026-04-10T22:19:07+05:30
New Revision: 027821479ee95d6ade3c5c488ae0b6c1b017fbfa
URL: https://github.com/llvm/llvm-project/commit/027821479ee95d6ade3c5c488ae0b6c1b017fbfa
DIFF: https://github.com/llvm/llvm-project/commit/027821479ee95d6ade3c5c488ae0b6c1b017fbfa.diff
LOG: [LifetimeSafety] Fix crash on ternary operator when one of the expressions is `throw` (#190345)
Ternary operator now doesn't flow origins from `[[noreturn]]` arms,
including `throw` statements.
Closes #183895
Added:
Modified:
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
clang/lib/Sema/AnalysisBasedWarnings.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 8fe2436b04086..2dbadb27981a7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -134,6 +134,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
// corresponding to the left-hand side is updated to be a "write", thereby
// exempting it from the check.
llvm::DenseMap<const Expr *, UseFact *> UseFacts;
+ const CFGBlock *CurrentBlock;
};
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index be01c7cbc8b92..fcc915711550a 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -103,6 +103,7 @@ void FactsGenerator::run() {
for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
CurrentBlockFacts.clear();
EscapesInCurrentBlock.clear();
+ CurrentBlock = Block;
if (Block == &Cfg.getEntry())
CurrentBlockFacts.append(PlaceholderLoanFacts.begin(),
PlaceholderLoanFacts.end());
@@ -422,12 +423,58 @@ void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
}
void FactsGenerator::VisitConditionalOperator(const ConditionalOperator *CO) {
- if (hasOrigins(CO)) {
- // Merge origins from both branches of the conditional operator.
- // We kill to clear the initial state and merge both origins into it.
- killAndFlowOrigin(*CO, *CO->getTrueExpr());
- flowOrigin(*CO, *CO->getFalseExpr());
+ if (!hasOrigins(CO))
+ return;
+
+ const Expr *TrueExpr = CO->getTrueExpr();
+ const Expr *FalseExpr = CO->getFalseExpr();
+
+ const auto Preds = CurrentBlock->preds();
+
+ // Skip origin flow from conditional operator arms that cannot produce the
+ // result value: throw arms and calls to noreturn functions.
+ bool TBHasEdge = true;
+ bool FBHasEdge = true;
+
+ switch (CurrentBlock->pred_size()) {
+ case 0:
+ return;
+ case 1: {
+ TBHasEdge = llvm::any_of(**Preds.begin(),
+ [ExpectedStmt = TrueExpr->IgnoreParenImpCasts()](
+ const CFGElement &Elt) {
+ if (auto CS = Elt.getAs<CFGStmt>())
+ return CS->getStmt() == ExpectedStmt;
+ return false;
+ });
+ FBHasEdge = !TBHasEdge;
+ break;
+ }
+ case 2: {
+ const auto *It = Preds.begin();
+ TBHasEdge = It->isReachable();
+ FBHasEdge = (++It)->isReachable();
+ break;
}
+ default:
+ llvm_unreachable("expected at most 2 predecessors");
+ return;
+ }
+
+ bool FirstFlow = true;
+ auto HandleFlow = [&](const Expr *E) {
+ if (FirstFlow) {
+ killAndFlowOrigin(*CO, *E);
+ FirstFlow = false;
+ } else {
+ flowOrigin(*CO, *E);
+ }
+ };
+
+ if (TBHasEdge)
+ HandleFlow(TrueExpr);
+ if (FBHasEdge)
+ HandleFlow(FalseExpr);
}
void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 37ed7488bb927..48957caf61e5a 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2922,7 +2922,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU,
if (!FD)
continue;
AnalysisDeclContext AC(nullptr, FD);
- AC.getCFGBuildOptions().PruneTriviallyFalseEdges = false;
+ AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
AC.getCFGBuildOptions().AddLifetime = true;
AC.getCFGBuildOptions().AddParameterLifetimes = true;
AC.getCFGBuildOptions().setAllAlwaysAdd();
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 63499b8b1d40e..941cedf22d1bb 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify=expected,function %s
// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=expected,tu %s
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -fcxx-exceptions -verify=expected,function %s
#include "Inputs/lifetime-analysis.h"
@@ -2430,3 +2431,58 @@ void owner_outlives_lifetimebound_source() {
}
} // namespace track_origins_for_lifetimebound_record_type
+
+namespace conditional_operator_control_flow {
+// https://github.com/llvm/llvm-project/issues/183895
+
+#ifdef __cpp_exceptions
+
+void throw_branches(bool cond, int *value) {
+ (void)(cond ? throw 1 : value);
+ (void)(cond ? throw 1 : throw 2);
+}
+
+void nested_throw_branches(bool cond, bool cond2, int *value) {
+ (void)(cond ? (cond2 ? throw 1 : value) : throw 2);
+ (void)(cond ? throw 1 : (cond2 ? value : throw 2));
+}
+
+#endif
+
+int *f(int *p [[clang::lifetimebound]]);
+[[noreturn]] int *noret_f(int *p [[clang::lifetimebound]]);
+
+
+constexpr bool kTrue = true;
+constexpr bool kFalse = false;
+
+int *constexpr_dead_false(int *num) {
+ int local = 0;
+ return kTrue ? num : f(&local);
+}
+
+int *constexpr_dead_nested(int *num) {
+ int local = 0;
+ return kTrue ? (kTrue ? num : f(&local)) : num;
+}
+
+int *constexpr_live_false(int *num) {
+ int local = 0;
+ return kFalse ? num : f(&local); // expected-warning {{address of stack memory is returned later}} // expected-note {{returned here}}
+}
+
+int *constexpr_live_nested(int *num) {
+ int local = 0;
+ return kTrue ? (kFalse ? num : f(&local)) : num; // expected-warning {{address of stack memory is returned later}} // expected-note {{returned here}}
+}
+int *noreturn_dead_false(bool cond, int *num) {
+ int local = 0;
+ return cond ? num : noret_f(&local);
+}
+
+int *noreturn_dead_nested(bool cond, bool cond2, int *num) {
+ int local = 0;
+ return cond ? (cond2 ? num : noret_f(&local)) : num;
+}
+
+} // namespace conditional_operator_control_flow
More information about the cfe-commits
mailing list