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

Reply via email to