https://github.com/anutosh491 created 
https://github.com/llvm/llvm-project/pull/204152

Fixes https://github.com/compiler-research/xeus-cpp/issues/431

When a function template is instantiated in clang-repl and the instantiation 
fails (e.g. a `static_assert` in the body), the session becomes poisoned for 
the rest of the interaction. Any subsequent cell — even completely unrelated 
ones like `int x = 10;` — would fail with a JIT "Symbols not found" error.

The root cause is a timing issue between when codegen emits IR and when the 
error actually fires.

When you call something like `f(1.0)` where f is a non-constexpr function 
template, clang doesn't instantiate the body immediately. It queues the 
instantiation as "deferred" and the parse loop moves on. At this point, codegen 
has already emitted a call instruction to `f<double>` into the current LLVM 
module — because the call expression was processed before any error fired.

The instantiation happens later, inside `ParseTopLevelDecl` when it hits 
`end-of-input `and calls `ActOnEndOfTranslationUnit` → 
`PerformPendingInstantiations`. That's where the `static_assert` fires. By 
then, the `InProcessPrintingASTConsumer `guard (`if (Diags.hasErrorOccurred())` 
return early) kicks in and the function body never reaches CodeGen. So the 
module ends up with a call to a symbol that has no definition.

I dumped Modules while executing code to show the same 

```
clang-repl> #include <type_traits>
clang-repl> #include <iostream>
clang-repl> template<typename T> T integral_div(T a, T b) {
                static_assert(std::is_integral<T>::value, "not an integral 
type");
                return a / b; }
clang-repl>              
clang-repl> integral_div<double>(8.0, 2.0)
In file included from <<< inputs >>>:1:
input_line_3:1:78: error: static assertion failed due to requirement 
'std::is_integral<double>::value':
      not an integral type
    1 |   ...integral_div(T a, T b) {                
static_assert(std::is_integral<T>::value, "not a...
      |                                                            
^~~~~~~~~~~~~~~~~~~~~~~~~~
input_line_4:1:1: note: in instantiation of function template specialization 
'integral_div<double>'
      requested here
    1 | integral_div<double>(8.0, 2.0)
      | ^
[clang-repl] parse error: module IR before cleanup ---
; ModuleID = 'incr_module_4'
source_filename = "incr_module_4"
target datalayout = 
"e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "arm64-apple-macosx26.0.0"

define internal void @__stmts__0() {
  %1 = bitcast i32 poison to i32
  %2 = call noundef double @_Z12integral_divIdET_S0_S0_(double noundef 
8.000000e+00, double noundef 2.000000e+00)
  call void (ptr, ptr, ptr, ...) @__clang_Interpreter_SetValueNoAlloc(ptr 
noundef inttoptr (i64 4547296960 to ptr), ptr noundef inttoptr (i64 4547297048 
to ptr), ptr noundef inttoptr (i64 37670594624 to ptr), double noundef %2)
}

declare void @__clang_Interpreter_SetValueNoAlloc(ptr noundef, ptr noundef, ptr 
noundef, ...) #0

declare noundef double @_Z12integral_divIdET_S0_S0_(double noundef, double 
noundef) #0

attributes #0 = { "frame-pointer"="non-leaf-no-reserve" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-cpu"="apple-m1" 
"target-features"="+aes,+altnzcv,+ccdp,+ccidx,+ccpp,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a"
 "tune-cpu"="apple-m5" }

!llvm.module.flags = !{!0}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 26, i32 5]}
[clang-repl] --- end module ---
error: Parsing failed.
clang-repl> 
clang-repl> 
clang-repl> int x = 10;
[clang-repl] module registered for PTU 4 (incr_module_4) ---
; ModuleID = 'incr_module_4'
source_filename = "incr_module_4"
target datalayout = 
"e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "arm64-apple-macosx26.0.0"

@x = global i32 10, align 4
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr 
} { i32 65535, ptr @_GLOBAL__sub_I_incr_module_4, ptr null }]

define internal void @__stmts__0() {
  %1 = call noundef double @_Z12integral_divIdET_S0_S0_(double noundef 
8.000000e+00, double noundef 2.000000e+00)
  call void (ptr, ptr, ptr, ...) @__clang_Interpreter_SetValueNoAlloc(ptr 
noundef inttoptr (i64 4547296960 to ptr), ptr noundef inttoptr (i64 4547297048 
to ptr), ptr noundef inttoptr (i64 37670594624 to ptr), double noundef %1)
  ret void
}

declare void @__clang_Interpreter_SetValueNoAlloc(ptr noundef, ptr noundef, ptr 
noundef, ...) #0

declare noundef double @_Z12integral_divIdET_S0_S0_(double noundef, double 
noundef) #0

; Function Attrs: noinline ssp uwtable(sync)
define internal void @_GLOBAL__sub_I_incr_module_4() #1 section 
"__TEXT,__StaticInit,regular,pure_instructions" {
  call void @__stmts__0()
  ret void
}

attributes #0 = { "frame-pointer"="non-leaf-no-reserve" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-cpu"="apple-m1" 
"target-features"="+aes,+altnzcv,+ccdp,+ccidx,+ccpp,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a"
 "tune-cpu"="apple-m5" }
attributes #1 = { noinline ssp uwtable(sync) 
"frame-pointer"="non-leaf-no-reserve" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-cpu"="apple-m1" 
"target-features"="+aes,+altnzcv,+ccdp,+ccidx,+ccpp,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a"
 "tune-cpu"="apple-m5" }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 26, i32 5]}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 1}
!3 = !{i32 7, !"frame-pointer", i32 4}
!4 = !{!"clang version 23.0.0git ([email protected]:anutosh491/llvm-project.git 
8abb6c1f71112016c9252ebfb19fd388a12b691e)"}
[clang-repl] --- end PTU module ---
JIT session error: Symbols not found: [ __Z12integral_divIdET_S0_S0_ ]
error: Failed to materialize symbols: { (main, { _x, $.incr_module_4.__inits.0, 
____orc_init_func.incr_module_4 }) }
```

As can be seen `__stmts__0` carrying `_Z12integral_divIdET_S0_S0_` is also a 
part of the IR generated from the next cell which is `int x = 10`

>From b0cc3a40d3132c442cfee9adf14dff0b036fdbc7 Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Tue, 16 Jun 2026 19:01:28 +0530
Subject: [PATCH] Fix module contamination after failed template instantiation

---
 clang/lib/Interpreter/IncrementalAction.cpp   |  5 +++--
 clang/lib/Interpreter/IncrementalAction.h     |  2 ++
 clang/lib/Interpreter/IncrementalParser.cpp   |  4 +++-
 .../recover-template-instantiation.cpp        | 22 +++++++++++++++++++
 4 files changed, 30 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Interpreter/recover-template-instantiation.cpp

diff --git a/clang/lib/Interpreter/IncrementalAction.cpp 
b/clang/lib/Interpreter/IncrementalAction.cpp
index d22031c8fa893..d9a7de25af147 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -53,7 +53,7 @@ IncrementalAction::IncrementalAction(CompilerInstance 
&Instance,
         }
         return Act;
       }()),
-      Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
+      Interp(I), CI(Instance), LLVMCtx(LLVMCtx), Consumer(std::move(Consumer)) 
{}
 
 std::unique_ptr<ASTConsumer>
 IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
@@ -114,7 +114,8 @@ std::unique_ptr<llvm::Module> 
IncrementalAction::GenModule() {
              CachedInCodeGenModule->ifunc_empty())) &&
            "CodeGen wrote to a readonly module");
     std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
-    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+    CG->StartModule("incr_module_" + std::to_string(ID++),
+                    M ? M->getContext() : LLVMCtx);
     return M;
   }
   return nullptr;
diff --git a/clang/lib/Interpreter/IncrementalAction.h 
b/clang/lib/Interpreter/IncrementalAction.h
index 725cdd0c27cf4..27daf15d8c91e 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -13,6 +13,7 @@
 #include "clang/Frontend/MultiplexConsumer.h"
 
 namespace llvm {
+class LLVMContext;
 class Module;
 }
 
@@ -35,6 +36,7 @@ class IncrementalAction : public WrapperFrontendAction {
   bool IsTerminating = false;
   Interpreter &Interp;
   [[maybe_unused]] CompilerInstance &CI;
+  llvm::LLVMContext &LLVMCtx;
   std::unique_ptr<ASTConsumer> Consumer;
 
   /// When CodeGen is created the first llvm::Module gets cached in many places
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index f6d2779d64b2b..b3af640f3cb21 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -85,7 +85,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
   DiagnosticsEngine &Diags = S.getDiagnostics();
   if (Diags.hasErrorOccurred()) {
     CleanUpPTU(C.getTranslationUnitDecl());
-
+    // Discard any partial CodeGen state to avoid contaminating the next cell.
+    Consumer->HandleTranslationUnit(C);
+    Act->GenModule();
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
     return llvm::make_error<llvm::StringError>("Parsing failed.",
diff --git a/clang/test/Interpreter/recover-template-instantiation.cpp 
b/clang/test/Interpreter/recover-template-instantiation.cpp
new file mode 100644
index 0000000000000..0cf8564cc8fe2
--- /dev/null
+++ b/clang/test/Interpreter/recover-template-instantiation.cpp
@@ -0,0 +1,22 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl 2>&1 | FileCheck %s
+
+// Verify that clang-repl recovers cleanly after a deferred template
+// instantiation error. The failed cell must not contaminate the CodeGen module
+// used by subsequent cells.
+
+extern "C" int printf(const char *, ...);
+
+template <typename T> T f(T a) {
+  static_assert(sizeof(T) == 0, "unsupported type");
+  return a;
+}
+
+f(1.0);
+// CHECK: error: static assertion failed
+
+int x = 10;
+auto r = printf("x = %d\n", x);
+// CHECK: x = 10
+
+%quit

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

Reply via email to