[llvm-branch-commits] [clang] [CFG] Fix temporary CXXDefaultInitExpr (PR #191786)

Utkarsh Saxena via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Apr 13 03:16:44 PDT 2026


https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/191786

>From 9855134d6e7ee0794f32d482d7b4d33950e93ce0 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 13 Apr 2026 10:10:43 +0000
Subject: [PATCH 1/2] [CFG] Fix temporary CXXDefaultInitExpr

---
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  1 +
 clang/lib/Analysis/CFG.cpp                    | 34 +++++++++++--------
 .../LifetimeSafety/FactsGenerator.cpp         |  5 +++
 clang/test/Sema/warn-lifetime-safety.cpp      | 12 +++++++
 4 files changed, 38 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 2dbadb27981a7..cab524f12bab3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -36,6 +36,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
   void VisitDeclStmt(const DeclStmt *DS);
   void VisitDeclRefExpr(const DeclRefExpr *DRE);
   void VisitCXXConstructExpr(const CXXConstructExpr *CCE);
+  void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *DIE);
   void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE);
   void VisitMemberExpr(const MemberExpr *ME);
   void VisitCallExpr(const CallExpr *CE);
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 543b9d8424488..cba6cec0f4f49 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1824,29 +1824,31 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
   if (!BuildOpts.AddInitializers)
     return Block;
 
-  bool HasTemporaries = false;
-
   // Destructors of temporaries in initialization expression should be called
   // after initialization finishes.
   Expr *Init = I->getInit();
   if (Init) {
-    HasTemporaries = isa<ExprWithCleanups>(Init);
+    Expr *ActualInit = Init;
+    if (BuildOpts.AddCXXDefaultInitExprInCtors) {
+      if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init))
+        ActualInit = DIE->getExpr();
+    }
+
+    bool HasTemporaries = isa<ExprWithCleanups>(ActualInit);
 
     if (HasTemporaries &&
         (BuildOpts.AddTemporaryDtors || BuildOpts.AddLifetime)) {
       // Generate destructors for temporaries in initialization expression.
       TempDtorContext Context;
-      VisitForTemporaries(cast<ExprWithCleanups>(Init)->getSubExpr(),
+      VisitForTemporaries(cast<ExprWithCleanups>(ActualInit)->getSubExpr(),
                           /*ExternallyDestructed=*/false, Context);
 
       addFullExprCleanupMarker(Context);
     }
-  }
 
-  autoCreateBlock();
-  appendInitializer(Block, I);
+    autoCreateBlock();
+    appendInitializer(Block, I);
 
-  if (Init) {
     // If the initializer is an ArrayInitLoopExpr, we want to extract the
     // initializer, that's used for each element.
     auto *AILEInit = extractElementInitializerFromNestedAILE(
@@ -1856,11 +1858,6 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
         ConstructionContextLayer::create(cfg->getBumpVectorContext(), I),
         AILEInit ? AILEInit : Init);
 
-    if (HasTemporaries) {
-      // For expression with temporaries go directly to subexpression to omit
-      // generating destructors for the second time.
-      return Visit(cast<ExprWithCleanups>(Init)->getSubExpr());
-    }
     if (BuildOpts.AddCXXDefaultInitExprInCtors) {
       if (CXXDefaultInitExpr *Default = dyn_cast<CXXDefaultInitExpr>(Init)) {
         // In general, appending the expression wrapped by a CXXDefaultInitExpr
@@ -1868,12 +1865,21 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
         // here is safe because there's only one initializer per field.
         autoCreateBlock();
         appendStmt(Block, Default);
-        if (Stmt *Child = Default->getExpr())
+        if (Stmt *Child = Default->getExpr()) {
+          if (HasTemporaries)
+            Child = cast<ExprWithCleanups>(Child)->getSubExpr();
           if (CFGBlock *R = Visit(Child))
             Block = R;
+        }
         return Block;
       }
     }
+
+    if (HasTemporaries) {
+      // For expression with temporaries go directly to subexpression to omit
+      // generating destructors for the second time.
+      return Visit(cast<ExprWithCleanups>(Init)->getSubExpr());
+    }
     return Visit(Init);
   }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 69a64d08e0385..be9dd03dfe3da 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -207,6 +207,11 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
                      /*IsGslConstruction=*/false);
 }
 
+void FactsGenerator::VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *DIE) {
+  if (const Expr *Init = DIE->getExpr())
+    killAndFlowOrigin(*DIE, *Init);
+}
+
 void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) {
   // Flows origins from the initializer expression to the field.
   // Example: `MyObj(std::string s) : view(s) {}`
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 07562c4b33f8e..b348bb362e6f9 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2615,3 +2615,15 @@ int *noreturn_dead_nested(bool cond, bool cond2, int *num) {
 }
 
 } // namespace conditional_operator_control_flow
+
+namespace CXXDefaultInitExprTests {
+struct Holder {
+  std::string_view view = std::string("temporary"); // expected-warning {{address of stack memory escapes to a field}} expected-note {{this field dangles}}
+  Holder() {}
+};
+
+void test() {
+  Holder h;
+  (void)h;
+}
+} // namespace CXXDefaultInitExprTests

>From aaba1c2b9d24397033b3af57997fda626b64bc92 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 13 Apr 2026 10:15:58 +0000
Subject: [PATCH 2/2] remove extra usage

---
 clang/test/Sema/warn-lifetime-safety.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index b348bb362e6f9..ee777af909eab 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2621,9 +2621,4 @@ struct Holder {
   std::string_view view = std::string("temporary"); // expected-warning {{address of stack memory escapes to a field}} expected-note {{this field dangles}}
   Holder() {}
 };
-
-void test() {
-  Holder h;
-  (void)h;
-}
 } // namespace CXXDefaultInitExprTests



More information about the llvm-branch-commits mailing list