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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
