https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/199588
>From 6098a2aa97e913b54675f6257e159d42ac1dcffc Mon Sep 17 00:00:00 2001 From: Zeyi Xu <[email protected]> Date: Tue, 26 May 2026 09:02:27 +0800 Subject: [PATCH 1/4] [LifetimeSafety] Avoid assert on variadic placement new --- .../LifetimeSafety/FactsGenerator.cpp | 45 ++++++++++--------- clang/test/Sema/warn-lifetime-safety.cpp | 11 +++++ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 8a7a4b79a1584..fa5a1aa4a7903 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -640,28 +640,29 @@ void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) { // And that the placement parameter num is 1, // that is to mostly limit to standard library placement new. if (NE->getNumPlacementArgs() == 1) { - if (const auto *Arg = NE->getOperatorNew() - ->getParamDecl(1) - ->getType() - ->getAs<PointerType>(); - Arg && Arg->isVoidPointerType()) { - // Use the placement argument before the implicit conversion to void*, so - // inner origins are still available. - const Expr *PlacementArg = NE->getPlacementArg(0); - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg); - ICE && ICE->getCastKind() == CK_BitCast && - PlacementArg->getType()->isVoidPointerType()) - PlacementArg = ICE->getSubExpr(); - OriginList *PlacementList = getOriginsList(*PlacementArg); - // FIXME: General placement arguments need separate handling to overwrite - // the right origins. - - // The pointer returned by placement new comes from the placement - // argument. - if (PlacementList) - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), - true)); + const FunctionDecl *OperatorNew = NE->getOperatorNew(); + if (OperatorNew->getNumParams() > 1) { + if (const auto *Arg = + OperatorNew->getParamDecl(1)->getType()->getAs<PointerType>(); + Arg && Arg->isVoidPointerType()) { + // Use the placement argument before the implicit conversion to void*, + // so inner origins are still available. + const Expr *PlacementArg = NE->getPlacementArg(0); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg); + ICE && ICE->getCastKind() == CK_BitCast && + PlacementArg->getType()->isVoidPointerType()) + PlacementArg = ICE->getSubExpr(); + OriginList *PlacementList = getOriginsList(*PlacementArg); + // FIXME: General placement arguments need separate handling to + // overwrite the right origins. + + // The pointer returned by placement new comes from the placement + // argument. + if (PlacementList) + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), + true)); + } } } else { const Loan *L = createLoan(FactMgr, NE); diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 70ec968dfd5c1..4bb7eb1e49437 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2982,6 +2982,17 @@ void placement_new_heap_then_delete_use_after_free() { (void)*p; // expected-note {{later used here}} } +struct PlacementArg {}; + +struct VariadicPlacementNew { + void *operator new(decltype(sizeof(0)), ...); +}; + +void variadic_placement_new() { + PlacementArg arg; + (void)new (arg) VariadicPlacementNew; +} + int* foo(int* x [[clang::lifetimebound]], int* y [[clang::lifetimebound]]); void placement_new_delete_result_of_lifetimebound_call() { >From d768bf23407fac57626e33b94886b7b2a7a460fd Mon Sep 17 00:00:00 2001 From: Zeyi Xu <[email protected]> Date: Wed, 27 May 2026 13:27:51 +0800 Subject: [PATCH 2/4] ~ --- clang/test/Sema/warn-lifetime-safety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 4bb7eb1e49437..bba18248a2af3 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2985,7 +2985,7 @@ void placement_new_heap_then_delete_use_after_free() { struct PlacementArg {}; struct VariadicPlacementNew { - void *operator new(decltype(sizeof(0)), ...); + void *operator new(std::size_t, ...); }; void variadic_placement_new() { >From c6f0593a6527ff3350b9eedb96f5179d4203ac54 Mon Sep 17 00:00:00 2001 From: Zeyi Xu <[email protected]> Date: Wed, 27 May 2026 16:31:11 +0800 Subject: [PATCH 3/4] address feedback --- .../Analyses/LifetimeSafety/FactsGenerator.h | 2 + .../LifetimeSafety/FactsGenerator.cpp | 57 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index 943bdd7199bf3..0b6d8c4fc026e 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -76,6 +76,8 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { void handlePointerArithmetic(const BinaryOperator *BO); + void handlePlacementNew(const CXXNewExpr *NE, OriginList *NewList); + void handleCXXCtorInitializer(const CXXCtorInitializer *CII); void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds); diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index fa5a1aa4a7903..969825320c285 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -631,6 +631,38 @@ void FactsGenerator::VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) { Dst->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true)); } +void FactsGenerator::handlePlacementNew(const CXXNewExpr *NE, + OriginList *NewList) { + if (NE->getNumPlacementArgs() != 1) + return; + + const FunctionDecl *OperatorNew = NE->getOperatorNew(); + if (OperatorNew->getNumParams() <= 1) + return; + + const auto *Arg = + OperatorNew->getParamDecl(1)->getType()->getAs<PointerType>(); + if (!Arg || !Arg->isVoidPointerType()) + return; + + // Use the placement argument before the implicit conversion to void*, so + // inner origins are still available. + const Expr *PlacementArg = NE->getPlacementArg(0); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg); + ICE && ICE->getCastKind() == CK_BitCast && + PlacementArg->getType()->isVoidPointerType()) + PlacementArg = ICE->getSubExpr(); + OriginList *PlacementList = getOriginsList(*PlacementArg); + // FIXME: General placement arguments need separate handling to overwrite + // the right origins. + + // The pointer returned by placement new comes from the placement + // argument. + if (PlacementList) + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), true)); +} + void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) { OriginList *NewList = getOriginsList(*NE); const Expr *Init = NE->getInitializer(); @@ -640,30 +672,7 @@ void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) { // And that the placement parameter num is 1, // that is to mostly limit to standard library placement new. if (NE->getNumPlacementArgs() == 1) { - const FunctionDecl *OperatorNew = NE->getOperatorNew(); - if (OperatorNew->getNumParams() > 1) { - if (const auto *Arg = - OperatorNew->getParamDecl(1)->getType()->getAs<PointerType>(); - Arg && Arg->isVoidPointerType()) { - // Use the placement argument before the implicit conversion to void*, - // so inner origins are still available. - const Expr *PlacementArg = NE->getPlacementArg(0); - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg); - ICE && ICE->getCastKind() == CK_BitCast && - PlacementArg->getType()->isVoidPointerType()) - PlacementArg = ICE->getSubExpr(); - OriginList *PlacementList = getOriginsList(*PlacementArg); - // FIXME: General placement arguments need separate handling to - // overwrite the right origins. - - // The pointer returned by placement new comes from the placement - // argument. - if (PlacementList) - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), - true)); - } - } + handlePlacementNew(NE, NewList); } else { const Loan *L = createLoan(FactMgr, NE); CurrentBlockFacts.push_back( >From b1b7ce1136a00642ad3d452f0696dea80a09d910 Mon Sep 17 00:00:00 2001 From: Zeyi Xu <[email protected]> Date: Wed, 27 May 2026 23:05:58 +0800 Subject: [PATCH 4/4] comments --- clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 969825320c285..daa6e15763074 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -633,6 +633,10 @@ void FactsGenerator::VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) { void FactsGenerator::handlePlacementNew(const CXXNewExpr *NE, OriginList *NewList) { + // Check if we have a placement new where the second argument is void*, to + // avoid flowing from non-pointer parameters, such as std::nothrow. + // And that the placement parameter num is 1, + // that is to mostly limit to standard library placement new. if (NE->getNumPlacementArgs() != 1) return; @@ -667,10 +671,6 @@ void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) { OriginList *NewList = getOriginsList(*NE); const Expr *Init = NE->getInitializer(); - // Check if we have a placement new where the second argument is void*, to - // avoid flowing from non-pointer parameters, such as std::nothrow. - // And that the placement parameter num is 1, - // that is to mostly limit to standard library placement new. if (NE->getNumPlacementArgs() == 1) { handlePlacementNew(NE, NewList); } else { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
