[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