[clang] [clang-repl] fix vtable symbol duplication error (closes #141039) (PR #185648)

via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 10 06:42:21 PDT 2026


https://github.com/conrade-ctc updated https://github.com/llvm/llvm-project/pull/185648

>From 05c2c79948547d0629f727111fe98502280f612f Mon Sep 17 00:00:00 2001
From: Emery Conrad <emery.conrad at chicagotrading.com>
Date: Tue, 10 Mar 2026 07:22:18 -0500
Subject: [PATCH] [clang-repl] fix vtable symbol duplication error

In incremental mode, emit by ExternalLinkage causes duplicate
symbol error. A single targeted change in getVTableLinkage()
fixes the issue by returning LinkOnceODRLinkage when
IncrementalExtensions is active. The JIT linker then keeps the first
definition a silently discards subsequent ones.

(Claude Sonnet 4.6 helped find this minimal delta)
---
 clang/lib/CodeGen/CGVTables.cpp               |  8 ++++++++
 clang/test/Interpreter/virtualdef-outside.cpp | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 clang/test/Interpreter/virtualdef-outside.cpp

diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 3891697a986e4..cadc98169226a 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -1151,6 +1151,14 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
                    ? llvm::GlobalVariable::LinkOnceODRLinkage
                    : llvm::Function::InternalLinkage;
 
+      // In incremental compilation, each partial translation unit is a separate
+      // LLVM module. Multiple PTUs may independently emit the vtable (once the
+      // key function's body is visible), causing duplicate symbol errors in the
+      // JIT. Use linkonce_odr so the JIT linker keeps the first definition and
+      // silently discards the rest, matching how template vtables work.
+      if (Context.getLangOpts().IncrementalExtensions)
+        return llvm::GlobalVariable::LinkOnceODRLinkage;
+
       return llvm::GlobalVariable::ExternalLinkage;
 
       case TSK_ImplicitInstantiation:
diff --git a/clang/test/Interpreter/virtualdef-outside.cpp b/clang/test/Interpreter/virtualdef-outside.cpp
new file mode 100644
index 0000000000000..a9198b3a22da5
--- /dev/null
+++ b/clang/test/Interpreter/virtualdef-outside.cpp
@@ -0,0 +1,19 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+// virtual functions defined outside of class had duplicate symbols:
+//     duplicate definition of symbol '__ZTV3Two' (i.e., vtable for Two)
+// see https://github.com/llvm/llvm-project/issues/141039.
+// fixed in PR #185648
+
+extern "C" int printf(const char *, ...);
+
+struct One { virtual void print() { printf("ONE\n"); } };
+One().print();
+// CHECK: ONE
+
+struct Two { virtual void print(); };
+void Two::print() { printf("TWO\n"); }
+Two().print();
+// CHECK: TWO
+
+%quit



More information about the cfe-commits mailing list