[clang] [CodeGen] Add CodeGenModule::forgetDecl to discard cached mangled-name state (PR #234493)

via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 24 22:17:45 PDT 2026


https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/234493

>From 1a31c54fbc77a50d5ae4afa7b04b2c9800ca21d4 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 21 Oct 2026 14:22:00 +0530
Subject: [PATCH 1/3] [CodeGen] Add CodeGenModule::forgetDecl to discard cached
 mangled-name state

Adds CodeGenModule::forgetDecl(GlobalDecl GD), exposed through CodeGenerator/ModuleBuilder, to clear cached entries in DeferredDecls and Manglings for a declaration.

This is useful for clang-repl, where a failed partial translation unit (PTU) may be rolled back and a new definition emitted with the same mangled name. Without clearing the old state, CodeGen may reuse stale entries from the discarded PTU.

This is also related to #223305 (module contamination after a failed template instantiation), where CodeGen holds onto state from a discarded PTU.
---
 clang/include/clang/CodeGen/ModuleBuilder.h | 5 +++++
 clang/lib/CodeGen/CodeGenModule.cpp         | 6 ++++++
 clang/lib/CodeGen/CodeGenModule.h           | 2 ++
 clang/lib/CodeGen/ModuleBuilder.cpp         | 6 ++++++
 4 files changed, 19 insertions(+)

diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h
index 99e3bdf013a8e8..672f97bbae6bdb 100644
--- a/clang/include/clang/CodeGen/ModuleBuilder.h
+++ b/clang/include/clang/CodeGen/ModuleBuilder.h
@@ -113,6 +113,11 @@ class CodeGenerator : public ASTConsumer {
   /// Create a new \c llvm::Module after calling HandleTranslationUnit. This
   /// enable codegen in interactive processing environments.
   llvm::Module* StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C);
+
+  /// Clear any CodeGen state associated with \p GD’s mangled name so that a
+  /// new declaration using the same name is treated as if CodeGen has
+  /// never seen it before.
+  void forgetDecl(GlobalDecl GD);
 };
 
 /// CreateLLVMCodeGen - Create a CodeGenerator instance.
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index a058d1fe7a2a3b..f26c09a9e7bb19 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -8665,6 +8665,12 @@ bool CodeGenModule::lookupRepresentativeDecl(StringRef MangledName,
   return true;
 }
 
+void CodeGenModule::forgetDecl(GlobalDecl GD) {
+  StringRef MangledName = getMangledName(GD);
+  DeferredDecls.erase(MangledName);
+  Manglings.erase(MangledName);
+}
+
 /// Emits metadata nodes associating all the global values in the
 /// current module with the Decls they came from.  This is useful for
 /// projects using IR gen as a subroutine.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 9e3f073c20f4a0..60fbc8167a8d7f 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -863,6 +863,8 @@ class CodeGenModule : public CodeGenTypeCache {
   bool lookupRepresentativeDecl(StringRef MangledName,
                                 GlobalDecl &Result) const;
 
+  void forgetDecl(GlobalDecl GD);
+
   llvm::Constant *getAtomicSetterHelperFnMap(QualType Ty) {
     return AtomicSetterHelperFnMap[Ty];
   }
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 0b00362487d2af..4be2aac9d6e8e2 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -155,6 +155,8 @@ namespace {
       return M.get();
     }
 
+    void forgetDecl(GlobalDecl GD) { Builder->forgetDecl(GD); }
+
     void Initialize(ASTContext &Context) override {
       Ctx = &Context;
 
@@ -394,6 +396,10 @@ llvm::Module *CodeGenerator::StartModule(llvm::StringRef ModuleName,
   return static_cast<CodeGeneratorImpl*>(this)->StartModule(ModuleName, C);
 }
 
+void CodeGenerator::forgetDecl(GlobalDecl GD) {
+  static_cast<CodeGeneratorImpl *>(this)->forgetDecl(GD);
+}
+
 std::unique_ptr<CodeGenerator>
 clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, llvm::StringRef ModuleName,
                          IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,

>From ebc9cf780d7bf970f1329a06d0ff73ccfacdce36 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sat, 24 Oct 2026 15:04:40 +0530
Subject: [PATCH 2/3] Apply CodeGenModule::forgetDecl and add test

---
 clang/lib/Interpreter/IncrementalParser.cpp   | 38 ++++++++++++++++---
 .../Interpreter/codegen-stale-entries.cpp     | 25 ++++++++++++
 2 files changed, 58 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/Interpreter/codegen-stale-entries.cpp

diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 3cfabb5e5daa18..a2e6b7ee4a4c48 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
+#include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -199,6 +200,7 @@ void IncrementalParser::withdrawMostRecentTU(
 class ASTDeclUnmerger : public DeclVisitor<ASTDeclUnmerger> {
   Sema &S;
   TranslationUnitDecl *DiscardedTU;
+  CodeGenerator *CodeGen;
 
   template <typename DeclT> void withdraw(Redeclarable<DeclT> *DBase) {
     if (NamedDecl *Prev = findSurvivor(static_cast<DeclT *>(DBase)))
@@ -275,20 +277,45 @@ class ASTDeclUnmerger : public DeclVisitor<ASTDeclUnmerger> {
       Primary->makeDeclVisibleInContext(Prev);
   }
 
+  void forgetMangledNames(std::initializer_list<GlobalDecl> GDs) {
+    if (!CodeGen)
+      return;
+    for (GlobalDecl GD : GDs)
+      CodeGen->forgetDecl(GD);
+  }
+
 public:
-  ASTDeclUnmerger(Sema &S, TranslationUnitDecl *DiscardedTU)
-      : S(S), DiscardedTU(DiscardedTU) {}
+  ASTDeclUnmerger(Sema &S, TranslationUnitDecl *DiscardedTU,
+                  CodeGenerator *CodeGen = nullptr)
+      : S(S), DiscardedTU(DiscardedTU), CodeGen(CodeGen) {}
 
   void VisitDecl(Decl *D) {
     if (auto *DC = dyn_cast<DeclContext>(D))
       VisitDeclContext(DC);
   }
 
-  void VisitFunctionDecl(FunctionDecl *D) { withdraw(D); }
+  void VisitFunctionDecl(FunctionDecl *D) {
+    forgetMangledNames({GlobalDecl(D)});
+    withdraw(D);
+  }
+  void VisitCXXConstructorDecl(CXXConstructorDecl *D) {
+    forgetMangledNames({GlobalDecl(D, Ctor_Complete), GlobalDecl(D, Ctor_Base),
+                        GlobalDecl(D, Ctor_Comdat)});
+    withdraw(D);
+  }
+  void VisitCXXDestructorDecl(CXXDestructorDecl *D) {
+    forgetMangledNames(
+        {GlobalDecl(D, Dtor_Deleting), GlobalDecl(D, Dtor_Complete),
+         GlobalDecl(D, Dtor_Base), GlobalDecl(D, Dtor_Comdat)});
+    withdraw(D);
+  }
   void VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { withdraw(D); }
   void VisitTypedefNameDecl(TypedefNameDecl *D) { withdraw(D); }
   void VisitUsingShadowDecl(UsingShadowDecl *D) { withdraw(D); }
-  void VisitVarDecl(VarDecl *D) { withdraw(D); }
+  void VisitVarDecl(VarDecl *D) {
+    forgetMangledNames({GlobalDecl(D)});
+    withdraw(D);
+  }
 
   void VisitTagDecl(TagDecl *D) {
     NamedDecl *Prev = findSurvivor(D);
@@ -325,7 +352,8 @@ class ASTDeclUnmerger : public DeclVisitor<ASTDeclUnmerger> {
 };
 
 void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
-  ASTDeclUnmerger(S, MostRecentTU).Visit(MostRecentTU);
+  ASTDeclUnmerger(S, MostRecentTU, Act ? Act->getCodeGen() : nullptr)
+      .Visit(MostRecentTU);
 
   // Lookup alone is not enough: the redeclaration chain still reaches these.
   withdrawMostRecentTU(MostRecentTU);
diff --git a/clang/test/Interpreter/codegen-stale-entries.cpp b/clang/test/Interpreter/codegen-stale-entries.cpp
new file mode 100644
index 00000000000000..ce54f071f2cf98
--- /dev/null
+++ b/clang/test/Interpreter/codegen-stale-entries.cpp
@@ -0,0 +1,25 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl 2>&1 | FileCheck %s
+//
+// This test demonstrates that stale entries in CodeGen keep foo and infn
+// reachable after %undo. It motivates removing stale entries from
+// CodeGenModule::DeferredDecls and CodeGenModule::Manglings via
+// CodeGenModule::forgetDecl (PR #234493).
+
+extern "C" int printf(const char *, ...);
+
+static int foo(int x);
+static int foo(int x) { return x + 1; }
+%undo
+auto sfr = printf("foo=%d\n", foo(10));
+// CHECK-NOT: foo=11
+// CHECK: JIT session error: Symbols not found
+
+inline int infn(int x);
+inline int infn(int x) { return x + 1; }
+%undo
+auto infnr = printf("infn=%d\n", infn(10));
+// CHECK-NOT: infn=11
+// CHECK: JIT session error: Symbols not found
+
+%quit

>From 90cd0c449f1cf161831fda266e1980cbceab578e Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 25 Oct 2026 10:47:16 +0530
Subject: [PATCH 3/3] add missing headers

---
 clang/lib/Interpreter/IncrementalParser.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index a2e6b7ee4a4c48..eeada8ab0a7695 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -19,6 +19,8 @@
 #include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/Basic/ABI.h"
 #include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"



More information about the cfe-commits mailing list