https://github.com/voyager-jhk updated 
https://github.com/llvm/llvm-project/pull/192588

>From 2d0a1dea30efbc78abcd59e73d41f35e9e94c02c Mon Sep 17 00:00:00 2001
From: voyager-jhk <[email protected]>
Date: Fri, 17 Apr 2026 11:39:28 +0800
Subject: [PATCH] [clang-repl] Roll back implicit instantiations on failed PTUs
 to prevent JIT starvation

Parsing failures cause CodeGen to discard the current module, but Sema retains 
implicitly instantiated bodies in the persistent AST. Subsequent valid uses 
skip emission, resulting in unresolved JIT symbols.

This patch implements a targeted, per-PTU rollback. It tracks new implicit 
instantiations in the ASTConsumer and resets their state during CleanUpPTU. 
This ensures Sema correctly re-emits them on the next valid use, serving as a 
lightweight DeclUnloader mitigation.

Fixes #146770
---
 clang/lib/Interpreter/IncrementalAction.cpp  | 17 ++++++++++++++---
 clang/lib/Interpreter/IncrementalAction.h    | 17 ++++++++++++++++-
 clang/lib/Interpreter/IncrementalParser.cpp  | 19 +++++++++++++++++++
 clang/lib/Sema/SemaExpr.cpp                  |  9 +++++++++
 clang/test/Interpreter/template-recovery.cpp | 12 ++++++++++++
 5 files changed, 70 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Interpreter/template-recovery.cpp

diff --git a/clang/lib/Interpreter/IncrementalAction.cpp 
b/clang/lib/Interpreter/IncrementalAction.cpp
index d22031c8fa893..08bdca02bf2da 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -9,6 +9,7 @@
 #include "IncrementalAction.h"
 
 #include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Decl.h"
 #include "clang/CodeGen/CodeGenAction.h"
 #include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
@@ -68,7 +69,8 @@ IncrementalAction::CreateASTConsumer(CompilerInstance & 
/*CI*/,
     return std::make_unique<MultiplexConsumer>(std::move(Cs));
   }
 
-  return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp);
+  return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp,
+                                                        *this);
 }
 
 void IncrementalAction::ExecuteAction() {
@@ -128,13 +130,22 @@ CodeGenerator *IncrementalAction::getCodeGen() const {
 }
 
 InProcessPrintingASTConsumer::InProcessPrintingASTConsumer(
-    std::unique_ptr<ASTConsumer> C, Interpreter &I)
-    : MultiplexConsumer(std::move(C)), Interp(I) {}
+    std::unique_ptr<ASTConsumer> C, Interpreter &I, IncrementalAction &Act)
+    : MultiplexConsumer(std::move(C)), Interp(I), Act(Act) {}
 
 bool InProcessPrintingASTConsumer::HandleTopLevelDecl(DeclGroupRef DGR) {
   if (DGR.isNull())
     return true;
 
+  // Track implicit instantiations that just acquired a body. Captured before
+  // the error bail-out so IncrementalParser can roll them back if the PTU
+  // fails.
+  for (Decl *D : DGR)
+    if (auto *FD = llvm::dyn_cast<FunctionDecl>(D))
+      if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation &&
+          FD->hasBody())
+        Act.trackInstantiation(FD);
+
   CompilerInstance *CI = Interp.getCompilerInstance();
   DiagnosticsEngine &Diags = CI->getDiagnostics();
   if (Diags.hasErrorOccurred())
diff --git a/clang/lib/Interpreter/IncrementalAction.h 
b/clang/lib/Interpreter/IncrementalAction.h
index 725cdd0c27cf4..8f1ab5ee2d849 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -11,6 +11,8 @@
 
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/MultiplexConsumer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
 
 namespace llvm {
 class Module;
@@ -20,6 +22,7 @@ namespace clang {
 
 class Interpreter;
 class CodeGenerator;
+class FunctionDecl;
 
 /// A custom action enabling the incremental processing functionality.
 ///
@@ -41,6 +44,10 @@ class IncrementalAction : public WrapperFrontendAction {
   /// and we must keep it alive.
   std::unique_ptr<llvm::Module> CachedInCodeGenModule;
 
+  /// Implicit instantiations of the current PTU. Tracked for rollback upon
+  /// parsing failure.
+  llvm::SmallVector<FunctionDecl *, 8> InstantiatedDecls;
+
 public:
   IncrementalAction(CompilerInstance &Instance, llvm::LLVMContext &LLVMCtx,
                     llvm::Error &Err, Interpreter &I,
@@ -74,13 +81,21 @@ class IncrementalAction : public WrapperFrontendAction {
 
   /// Generate an LLVM module for the most recent parsed input.
   std::unique_ptr<llvm::Module> GenModule();
+
+  void trackInstantiation(FunctionDecl *FD) { InstantiatedDecls.push_back(FD); 
}
+  llvm::ArrayRef<FunctionDecl *> getInstantiatedDecls() const {
+    return InstantiatedDecls;
+  }
+  void resetInstantiationTracking() { InstantiatedDecls.clear(); }
 };
 
 class InProcessPrintingASTConsumer final : public MultiplexConsumer {
   Interpreter &Interp;
+  IncrementalAction &Act;
 
 public:
-  InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I);
+  InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I,
+                               IncrementalAction &Act);
 
   bool HandleTopLevelDecl(DeclGroupRef DGR) override;
 };
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index f6d2779d64b2b..5d4cccd79acff 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -15,6 +15,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -56,6 +57,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
                                                            /*AtEndOfTU=*/true);
   Sema::LocalEagerInstantiationScope LocalInstantiations(S, 
/*AtEndOfTU=*/true);
 
+  // Reset the instantiation tracker for the new PTU.
+  Act->resetInstantiationTracking();
+
   // Add a new PTU.
   ASTContext &C = S.getASTContext();
   C.addTranslationUnitDecl();
@@ -86,6 +90,21 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
   if (Diags.hasErrorOccurred()) {
     CleanUpPTU(C.getTranslationUnitDecl());
 
+    // Roll back implicit instantiations to allow clean re-emission on
+    // subsequent uses.
+    for (FunctionDecl *FD : Act->getInstantiatedDecls()) {
+      if (FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
+        continue;
+      FD->setBody(nullptr);
+      FD->setWillHaveBody(false);
+      FD->setInstantiationIsPending(false);
+      if (auto *FTSI = FD->getTemplateSpecializationInfo())
+        FTSI->setPointOfInstantiation(SourceLocation());
+      else if (auto *MSI = FD->getMemberSpecializationInfo())
+        MSI->setPointOfInstantiation(SourceLocation());
+    }
+    Act->resetInstantiationTracking();
+
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
     return llvm::make_error<llvm::StringError>("Parsing failed.",
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 521a8516ac179..7fae16dc07bf0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19061,6 +19061,15 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, 
FunctionDecl *Func,
         }
       }
     });
+  } else if (NeedDefinition && getLangOpts().IncrementalExtensions &&
+             !Diags.hasErrorOccurred()) {
+    const FunctionDecl *Def = nullptr;
+    if (Func->getBody(Def) &&
+        Def->getTemplateSpecializationKind() == TSK_ImplicitInstantiation &&
+        !Def->isInvalidDecl()) {
+      Consumer.HandleTopLevelDecl(
+          DeclGroupRef(const_cast<FunctionDecl *>(Def)));
+    }
   }
 
   // If a constructor was defined in the context of a default parameter
diff --git a/clang/test/Interpreter/template-recovery.cpp 
b/clang/test/Interpreter/template-recovery.cpp
new file mode 100644
index 0000000000000..713b3c5f7712c
--- /dev/null
+++ b/clang/test/Interpreter/template-recovery.cpp
@@ -0,0 +1,12 @@
+// REQUIRES: host-supports-jit
+// RUN: clang-repl -Xcc -fno-color-diagnostics < %s 2>&1 | FileCheck %s
+
+template <typename T> T my_pow(T a, T b) { return a * b; }
+
+(10-)*my_pow(2, 2);
+// CHECK: error: expected expression
+// CHECK: error: Parsing failed.
+
+int x = my_pow(2, 2);
+// CHECK-NOT: JIT session error
+// CHECK-NOT: Failed to materialize symbols

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to