https://github.com/voyager-jhk updated https://github.com/llvm/llvm-project/pull/192588
>From cd0a334921b7dc67a921625a9f61d292cd3507d6 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/test/Interpreter/template-recovery.cpp | 12 ++++++++++++ 4 files changed, 61 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/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
