https://github.com/kikairoya updated 
https://github.com/llvm/llvm-project/pull/168171

>From 3234c2685a86561a3df695f6a8e4b6c55c1a7f5c Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Sat, 15 Nov 2025 11:25:23 +0900
Subject: [PATCH 1/9] pretest

---
 ...t_instantiation.exclude_from_dllexport.cpp | 64 +++++++++++++++++++
 ...t_instantiation.exclude_from_dllimport.cpp | 50 +++++++++++++++
 2 files changed, 114 insertions(+)
 create mode 100644 
clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 create mode 100644 
clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
new file mode 100644
index 0000000000000..6b1bd83f6d69b
--- /dev/null
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+
+// Test that __declspec(dllexport) doesn't instantiate entities marked with
+// the exclude_from_explicit_instantiation attribute unless marked as 
dllexport explicitly.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION 
__attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct C {
+  // This will be instantiated explicitly as an exported function because it
+  // inherits dllexport from the class instantiation.
+  void to_be_exported() noexcept;
+
+  // This will be instantiated implicitly as an exported function because it is
+  // marked as dllexport explicitly.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void 
to_be_exported_explicitly() noexcept;
+
+  // This will be instantiated implicitly as an exported function 
unintentionally.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported() noexcept;
+
+  // This won't be instantiated.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
+};
+
+template <class T> void C<T>::to_be_exported() noexcept {}
+template <class T> void C<T>::to_be_exported_explicitly() noexcept {}
+template <class T> void C<T>::not_to_be_exported() noexcept {}
+template <class T> void C<T>::not_to_be_instantiated() noexcept {}
+
+// MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?not_to_be_instantiated@?$C@H@@QEAAXXZ" = comdat any
+// GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
+// GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
+// GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
+// GNU: $_ZN1CIiE22not_to_be_instantiatedEv = comdat any
+
+// MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$C@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE14to_be_exportedEv
+template struct __declspec(dllexport) C<int>;
+
+void use() {
+  C<int> c;
+
+  // MSC: call void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
+  // GNU: call void @_ZN1CIiE25to_be_exported_explicitlyEv
+  c.to_be_exported_explicitly(); // implicitly instantiated here
+
+  // MSC: call void @"?not_to_be_exported@?$C@H@@QEAAXXZ"
+  // GNU: call void @_ZN1CIiE18not_to_be_exportedEv
+  c.not_to_be_exported(); // implicitly instantiated here
+};
+
+// MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE25to_be_exported_explicitlyEv
+
+// MSC: define weak_odr dso_local dllexport void 
@"?not_to_be_exported@?$C@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport void 
@_ZN1CIiE18not_to_be_exportedEv
+
+// MSC: define weak_odr dso_local dllexport void 
@"?not_to_be_instantiated@?$C@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport void 
@_ZN1CIiE22not_to_be_instantiatedEv
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
new file mode 100644
index 0000000000000..adc420f37bbc6
--- /dev/null
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+
+// Test that __declspec(dllimport) doesn't instantiate entities marked with
+// the exclude_from_explicit_instantiation attribute unless marked as 
dllimport explicitly.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION 
__attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct C {
+  // This will be instantiated explicitly as an imported function because it
+  // inherits dllimport from the class instantiation.
+  void to_be_imported() noexcept;
+
+  // Per-member dllimport in explicitly dllimport-ed template instantiation is 
not allowed.
+  //EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void 
to_be_imported_explicitly() noexcept;
+
+  // This will be instantiated implicitly as an imported function 
unintentionally.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept;
+
+  // This won't be instantiated.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
+};
+
+template <class T> void C<T>::to_be_imported() noexcept {}
+template <class T> void C<T>::not_to_be_imported() noexcept {}
+template <class T> void C<T>::not_to_be_instantiated() noexcept {}
+
+extern template struct __declspec(dllimport) C<int>;
+
+void use() {
+  C<int> c;
+
+  // MSC: call void @"?to_be_imported@?$C@H@@QEAAXXZ"
+  // GNU: call void @_ZN1CIiE14to_be_importedEv
+  c.to_be_imported();
+
+  //c.to_be_imported_explicitly();
+
+  // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
+  // GNU: call void @_ZN1CIiE18not_to_be_importedEv
+  c.not_to_be_imported(); // implicitly instantiated here
+};
+
+// MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv
+
+// MSC: declare dllimport void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv

>From 1e558daf82c6750b14b6ea273175d29279cce450 Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Sat, 15 Nov 2025 11:25:24 +0900
Subject: [PATCH 2/9] [Clang] Apply exclude_from_explicit_instantiation to
 dllimport/dllexport

Attaching `__declspec(dllexport/dllimport)` to explicit instantiation
declaration made its whole member instantiated even if they were
`__attribute__((__exclude_from_explicit_instantation__))`.
Such members should not be instantiated nor be exported to avoid
symbol leakage or duplication.
---
 clang/lib/Sema/SemaDeclCXX.cpp | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index aa36a79142e52..fe732a1328fab 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6551,6 +6551,8 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
     return;
   }
 
+  TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind();
+
   if (Context.getTargetInfo().shouldDLLImportComdatSymbols() &&
       !ClassAttr->isInherited()) {
     // Diagnose dll attributes on members of class with dll attribute.
@@ -6561,6 +6563,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
       if (!MemberAttr || MemberAttr->isInherited() || Member->isInvalidDecl())
         continue;
 
+      if ((TSK == TSK_ExplicitInstantiationDeclaration ||
+           TSK == TSK_ExplicitInstantiationDefinition) &&
+          Member->hasAttr<ExcludeFromExplicitInstantiationAttr>())
+        continue;
+
       Diag(MemberAttr->getLocation(),
              diag::err_attribute_dll_member_of_dll_class)
           << MemberAttr << ClassAttr;
@@ -6583,8 +6590,6 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
       !ClassExported &&
       cast<DLLImportAttr>(ClassAttr)->wasPropagatedToBaseTemplate();
 
-  TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind();
-
   // Ignore explicit dllexport on explicit class template instantiation
   // declarations, except in MinGW mode.
   if (ClassExported && !ClassAttr->isInherited() &&
@@ -6601,6 +6606,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
   // seem to be true in practice?
 
   for (Decl *Member : Class->decls()) {
+    if ((TSK == TSK_ExplicitInstantiationDeclaration ||
+         TSK == TSK_ExplicitInstantiationDefinition) &&
+        Member->hasAttr<ExcludeFromExplicitInstantiationAttr>())
+      continue;
+
     VarDecl *VD = dyn_cast<VarDecl>(Member);
     CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member);
 

>From d7e93e8c139252f102bd78fda4a3ab252bf93ab2 Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Sat, 15 Nov 2025 11:25:24 +0900
Subject: [PATCH 3/9] update test

---
 ...t_instantiation.exclude_from_dllexport.cpp | 11 +++-------
 ...t_instantiation.exclude_from_dllimport.cpp | 20 +++++++++++++------
 2 files changed, 17 insertions(+), 14 deletions(-)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
index 6b1bd83f6d69b..0a94bd4b1f6b6 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -17,7 +17,7 @@ struct C {
   // marked as dllexport explicitly.
   EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void 
to_be_exported_explicitly() noexcept;
 
-  // This will be instantiated implicitly as an exported function 
unintentionally.
+  // This will be instantiated implicitly but won't be exported.
   EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported() noexcept;
 
   // This won't be instantiated.
@@ -32,11 +32,9 @@ template <class T> void C<T>::not_to_be_instantiated() 
noexcept {}
 // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
-// MSC: $"?not_to_be_instantiated@?$C@H@@QEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
 // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
-// GNU: $_ZN1CIiE22not_to_be_instantiatedEv = comdat any
 
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$C@H@@QEAAXXZ"
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE14to_be_exportedEv
@@ -57,8 +55,5 @@ void use() {
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE25to_be_exported_explicitlyEv
 
-// MSC: define weak_odr dso_local dllexport void 
@"?not_to_be_exported@?$C@H@@QEAAXXZ"
-// GNU: define weak_odr dso_local dllexport void 
@_ZN1CIiE18not_to_be_exportedEv
-
-// MSC: define weak_odr dso_local dllexport void 
@"?not_to_be_instantiated@?$C@H@@QEAAXXZ"
-// GNU: define weak_odr dso_local dllexport void 
@_ZN1CIiE22not_to_be_instantiatedEv
+// MSC: define linkonce_odr dso_local void 
@"?not_to_be_exported@?$C@H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_exportedEv
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index adc420f37bbc6..b070259c2f048 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -13,10 +13,11 @@ struct C {
   // inherits dllimport from the class instantiation.
   void to_be_imported() noexcept;
 
-  // Per-member dllimport in explicitly dllimport-ed template instantiation is 
not allowed.
-  //EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void 
to_be_imported_explicitly() noexcept;
+  // This will be instantiated implicitly as an imported function because it is
+  // marked as dllimport explicitly.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void 
to_be_imported_explicitly() noexcept;
 
-  // This will be instantiated implicitly as an imported function 
unintentionally.
+  // This will be instantiated implicitly but won't be imported.
   EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept;
 
   // This won't be instantiated.
@@ -27,6 +28,8 @@ template <class T> void C<T>::to_be_imported() noexcept {}
 template <class T> void C<T>::not_to_be_imported() noexcept {}
 template <class T> void C<T>::not_to_be_instantiated() noexcept {}
 
+// MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
+// GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
 extern template struct __declspec(dllimport) C<int>;
 
 void use() {
@@ -36,7 +39,9 @@ void use() {
   // GNU: call void @_ZN1CIiE14to_be_importedEv
   c.to_be_imported();
 
-  //c.to_be_imported_explicitly();
+  // MSC: call void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ"
+  // GNU: call void @_ZN1CIiE25to_be_imported_explicitlyEv
+  c.to_be_imported_explicitly(); // implicitly instantiated here
 
   // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
   // GNU: call void @_ZN1CIiE18not_to_be_importedEv
@@ -46,5 +51,8 @@ void use() {
 // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
 // GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv
 
-// MSC: declare dllimport void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
-// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE25to_be_imported_explicitlyEv
+
+// MSC: define linkonce_odr dso_local void 
@"?not_to_be_imported@?$C@H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv

>From 94d7cf7933a73410548dc787baf6d9eac5b0d60f Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Sun, 30 Nov 2025 10:03:12 +0900
Subject: [PATCH 4/9] add --implicit-check-not=dll{{in|ex}}port

---
 ...it_instantiation.exclude_from_dllexport.cpp | 18 ++++++++++++++----
 ...it_instantiation.exclude_from_dllimport.cpp | 16 ++++++++++++----
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
index 0a94bd4b1f6b6..a2d8e37acb626 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -1,10 +1,18 @@
-// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
-// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
-// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ 
--implicit-check-not=dllexport
+// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ 
--implicit-check-not=dllexport
+// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ 
--implicit-check-not=dllexport
 
 // Test that __declspec(dllexport) doesn't instantiate entities marked with
 // the exclude_from_explicit_instantiation attribute unless marked as 
dllexport explicitly.
 
+// MSC: ModuleID = {{.*}}exclude_from_dllexport.cpp
+// MSC: source_filename = {{.*}}exclude_from_dllexport.cpp
+// GNU: ModuleID = {{.*}}exclude_from_dllexport.cpp
+// GNU: source_filename = {{.*}}exclude_from_dllexport.cpp
+
 #define EXCLUDE_FROM_EXPLICIT_INSTANTIATION 
__attribute__((exclude_from_explicit_instantiation))
 
 template <class T>
@@ -36,7 +44,9 @@ template <class T> void C<T>::not_to_be_instantiated() 
noexcept {}
 // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
 
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$C@H@@QEAAAEAU0@AEBU0@@Z"
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$C@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1CIiEaSERKS0_
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE14to_be_exportedEv
 template struct __declspec(dllexport) C<int>;
 
@@ -50,7 +60,7 @@ void use() {
   // MSC: call void @"?not_to_be_exported@?$C@H@@QEAAXXZ"
   // GNU: call void @_ZN1CIiE18not_to_be_exportedEv
   c.not_to_be_exported(); // implicitly instantiated here
-};
+}
 
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE25to_be_exported_explicitlyEv
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index b070259c2f048..fa44f3aaa6e8a 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -1,10 +1,18 @@
-// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
-// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
-// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
+// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ 
--implicit-check-not=dllimport
+// RUN: %clang_cc1 -triple x86_64-mingw                 -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ 
--implicit-check-not=dllimport
+// RUN: %clang_cc1 -triple x86_64-cygwin                -emit-llvm -o - %s | \
+// RUN:     FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ 
--implicit-check-not=dllimport
 
 // Test that __declspec(dllimport) doesn't instantiate entities marked with
 // the exclude_from_explicit_instantiation attribute unless marked as 
dllimport explicitly.
 
+// MSC: ModuleID = {{.*}}exclude_from_dllimport.cpp
+// MSC: source_filename = {{.*}}exclude_from_dllimport.cpp
+// GNU: ModuleID = {{.*}}exclude_from_dllimport.cpp
+// GNU: source_filename = {{.*}}exclude_from_dllimport.cpp
+
 #define EXCLUDE_FROM_EXPLICIT_INSTANTIATION 
__attribute__((exclude_from_explicit_instantiation))
 
 template <class T>
@@ -46,7 +54,7 @@ void use() {
   // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
   // GNU: call void @_ZN1CIiE18not_to_be_importedEv
   c.not_to_be_imported(); // implicitly instantiated here
-};
+}
 
 // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
 // GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv

>From bdbeeac2ccefd653641ab115edf58408d8c12d91 Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Mon, 1 Dec 2025 21:15:38 +0900
Subject: [PATCH 5/9] add tests for the class-level attributes

---
 ...t_instantiation.exclude_from_dllexport.cpp | 32 +++++++++++++++++
 ...t_instantiation.exclude_from_dllimport.cpp | 34 +++++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
index a2d8e37acb626..eba5f24f2d5ae 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -37,12 +37,29 @@ template <class T> void C<T>::to_be_exported_explicitly() 
noexcept {}
 template <class T> void C<T>::not_to_be_exported() noexcept {}
 template <class T> void C<T>::not_to_be_instantiated() noexcept {}
 
+// Attach the attribute to class template declaration instead of instantiation 
declaration.
+template <class T>
+struct __declspec(dllexport) D {
+  // This should be exported by the class-level attribute.
+  void to_be_exported() noexcept;
+
+  // This also should be exported by the class-level attribute but currently 
not.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_exported() noexcept;
+};
+
+template <class T> void D<T>::to_be_exported() noexcept {}
+template <class T> void D<T>::also_to_be_exported() noexcept {}
+
 // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$D@H@@QEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?also_to_be_exported@?$D@H@@QEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
+// GNU: $_ZN1DIiE14to_be_exportedEv = comdat any
 // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
+// GNU: $_ZN1DIiE19also_to_be_exportedEv = comdat any
 
 // MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$C@H@@QEAAAEAU0@AEBU0@@Z"
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$C@H@@QEAAXXZ"
@@ -50,6 +67,12 @@ template <class T> void C<T>::not_to_be_instantiated() 
noexcept {}
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1CIiE14to_be_exportedEv
 template struct __declspec(dllexport) C<int>;
 
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$D@H@@QEAAAEAU0@AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$D@H@@QEAAXXZ"
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1DIiE14to_be_exportedEv
+template struct D<int>;
+
 void use() {
   C<int> c;
 
@@ -60,6 +83,12 @@ void use() {
   // MSC: call void @"?not_to_be_exported@?$C@H@@QEAAXXZ"
   // GNU: call void @_ZN1CIiE18not_to_be_exportedEv
   c.not_to_be_exported(); // implicitly instantiated here
+
+  D<int> d;
+
+  // MSC: call void @"?also_to_be_exported@?$D@H@@QEAAXXZ"
+  // GNU: call void @_ZN1DIiE19also_to_be_exportedEv
+  d.also_to_be_exported(); // implicitly instantiated here
 }
 
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
@@ -67,3 +96,6 @@ void use() {
 
 // MSC: define linkonce_odr dso_local void 
@"?not_to_be_exported@?$C@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_exportedEv
+
+// MSC: define linkonce_odr dso_local void 
@"?also_to_be_exported@?$D@H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index fa44f3aaa6e8a..f263ff070d852 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -36,10 +36,28 @@ template <class T> void C<T>::to_be_imported() noexcept {}
 template <class T> void C<T>::not_to_be_imported() noexcept {}
 template <class T> void C<T>::not_to_be_instantiated() noexcept {}
 
+// Attach the attribute to class template declaration instead of instantiation 
declaration.
+template <class T>
+struct __declspec(dllimport) D {
+  // This will be imported by the class-level attribute.
+  void to_be_imported() noexcept;
+
+  // This also should be imported by the class-level attribute but currently 
not.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_imported() noexcept;
+};
+
+template <class T> void D<T>::to_be_imported() noexcept {}
+template <class T> void D<T>::also_to_be_imported() noexcept {}
+
 // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
+// MSC: $"?also_to_be_imported@?$D@H@@QEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
+// GNU: $_ZN1DIiE19also_to_be_importedEv = comdat any
+
 extern template struct __declspec(dllimport) C<int>;
 
+extern template struct D<int>;
+
 void use() {
   C<int> c;
 
@@ -54,6 +72,16 @@ void use() {
   // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
   // GNU: call void @_ZN1CIiE18not_to_be_importedEv
   c.not_to_be_imported(); // implicitly instantiated here
+
+  D<int> d;
+
+  // MSC: call void @"?to_be_imported@?$D@H@@QEAAXXZ"
+  // GNU: call void @_ZN1DIiE14to_be_importedEv
+  d.to_be_imported(); // implicitly instantiated here
+
+  // MSC: call void @"?also_to_be_imported@?$D@H@@QEAAXXZ"
+  // GNU: call void @_ZN1DIiE19also_to_be_importedEv
+  d.also_to_be_imported(); // implicitly instantiated here
 }
 
 // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
@@ -64,3 +92,9 @@ void use() {
 
 // MSC: define linkonce_odr dso_local void 
@"?not_to_be_imported@?$C@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
+
+// MSC: declare dllimport void @"?to_be_imported@?$D@H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1DIiE14to_be_importedEv
+
+// MSC: define linkonce_odr dso_local void 
@"?also_to_be_imported@?$D@H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv

>From bf8df954888217b07b5c5f99f0b98cb5d58e0e6d Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Sun, 30 Nov 2025 07:37:12 +0900
Subject: [PATCH 6/9] add tests for virtual member functions

---
 ...t_instantiation.exclude_from_dllexport.cpp | 64 +++++++++++++++++
 ...t_instantiation.exclude_from_dllimport.cpp | 69 +++++++++++++++++++
 2 files changed, 133 insertions(+)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
index eba5f24f2d5ae..4bff5a828f34c 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -50,16 +50,54 @@ struct __declspec(dllexport) D {
 template <class T> void D<T>::to_be_exported() noexcept {}
 template <class T> void D<T>::also_to_be_exported() noexcept {}
 
+// Interaction with VTables.
+template <class T>
+struct E {
+  // This will be instanciated by the explicit template instantiation 
definition.
+  virtual void to_be_exported() noexcept;
+
+  // This will be instantiated by the VTable definition, regardless of
+  // `exclude_from_explicit_instantiation`.
+  // The dllexport attribute won't be inherited.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated() 
noexcept;
+
+  // This too, but will be exported by the member attribute.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) virtual void 
to_be_exported_explicitly() noexcept;
+};
+
+template <class T> void E<T>::to_be_exported() noexcept {}
+template <class T> void E<T>::to_be_instantiated() noexcept {}
+template <class T> void E<T>::to_be_exported_explicitly() noexcept {}
+
 // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?to_be_exported@?$D@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$E@H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$E@I@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?also_to_be_exported@?$D@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E@H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$E@H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E@I@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$E@I@@UEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
 // GNU: $_ZN1DIiE14to_be_exportedEv = comdat any
+// GNU: $_ZN1EIiE14to_be_exportedEv = comdat any
+// GNU: $_ZN1EIjE14to_be_exportedEv = comdat any
 // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
 // GNU: $_ZN1DIiE19also_to_be_exportedEv = comdat any
+// GNU: $_ZN1EIiE18to_be_instantiatedEv = comdat any
+// GNU: $_ZN1EIiE25to_be_exported_explicitlyEv = comdat any
+// GNU: $_ZN1EIjE18to_be_instantiatedEv = comdat any
+// GNU: $_ZN1EIjE25to_be_exported_explicitlyEv = comdat any
+
+// MSC: @0 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@H@@6B@")
+// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@I@@6B@")
+// MSC: @"??_7?$E@H@@6B@" = dllexport unnamed_addr
+// MSC: @"??_7?$E@I@@6B@" = unnamed_addr
+// GNU:@_ZTV1EIiE = weak_odr dso_local dllexport unnamed_addr constant {{.*}}, 
comdat
+// GNU:@_ZTV1EIjE = weak_odr dso_local unnamed_addr constant {{.*}}, comdat
 
 // MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$C@H@@QEAAAEAU0@AEBU0@@Z"
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$C@H@@QEAAXXZ"
@@ -73,6 +111,22 @@ template struct __declspec(dllexport) C<int>;
 // GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1DIiE14to_be_exportedEv
 template struct D<int>;
 
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$E@H@@QEAAAEAU0@AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E@H@@QEAA@XZ"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??0?$E@H@@QEAA@AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$E@H@@UEAAXXZ"
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1EIiEaSERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC2Ev
+// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1Ev
+// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC2ERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1ERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1EIiE14to_be_exportedEv
+template struct __declspec(dllexport) E<int>;
+
+// MSC: define weak_odr dso_local{{.*}} void @"?to_be_exported@?$E@I@@UEAAXXZ"
+// GNU: define weak_odr dso_local{{.*}} void @_ZN1EIjE14to_be_exportedEv
+template struct E<unsigned int>;
+
 void use() {
   C<int> c;
 
@@ -99,3 +153,13 @@ void use() {
 
 // MSC: define linkonce_odr dso_local void 
@"?also_to_be_exported@?$D@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv
+
+// MSC: define linkonce_odr dso_local void 
@"?to_be_instantiated@?$E@H@@UEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void 
@"?to_be_exported_explicitly@?$E@H@@UEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1EIiE18to_be_instantiatedEv
+// GNU: define weak_odr dso_local dllexport void 
@_ZN1EIiE25to_be_exported_explicitlyEv
+
+// MSC: define linkonce_odr dso_local void 
@"?to_be_instantiated@?$E@I@@UEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void 
@"?to_be_exported_explicitly@?$E@I@@UEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1EIjE18to_be_instantiatedEv
+// GNU: define weak_odr dso_local dllexport void 
@_ZN1EIjE25to_be_exported_explicitlyEv
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index f263ff070d852..6cb169bb5a4d8 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -49,15 +49,59 @@ struct __declspec(dllimport) D {
 template <class T> void D<T>::to_be_imported() noexcept {}
 template <class T> void D<T>::also_to_be_imported() noexcept {}
 
+// Interaction with VTables.
+template <class T>
+struct E {
+  // For the MSVC ABI: this constructor causes implicit instantiation of
+  // the VTable, which should trigger instantiating all virtual member
+  // functions regardless `exclude_from_explicit_instantiation` but currently 
not.
+  // For the Itanium ABI: Emitting the VTable is suppressed by implicit
+  // instantiation declaration so virtual member functions won't be 
instantiated.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION explicit E(int);
+
+  // This constructor doesn't trigger the instantiation of the VTable.
+  // In this case, declaration of virtual member functions are absent too.
+  explicit E(long);
+
+  // The body of this shouldn't be emitted since instantiation is suppressed
+  // by the explicit instantiation declaration.
+  virtual void to_be_imported() noexcept;
+
+  // The body of this should be emitted if the VTable is instantiated, even if
+  // the instantiation of this class template is declared with dllimport.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated() 
noexcept;
+
+  // The body of this shouldn't be emitted since that comes from an external 
DLL.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) virtual void 
to_be_imported_explicitly() noexcept;
+
+};
+
+template <class T> E<T>::E(int) {}
+template <class T> E<T>::E(long) {}
+template <class T> void E<T>::to_be_imported() noexcept {}
+template <class T> void E<T>::to_be_instantiated() noexcept {}
+
 // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?also_to_be_imported@?$D@H@@QEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
 // GNU: $_ZN1DIiE19also_to_be_importedEv = comdat any
+// GNU: @_ZTV1EIiE = external dllimport unnamed_addr
+// GNU: @_ZTV1EIjE = external unnamed_addr
+
+// MSC: @0 = private unnamed_addr constant {{.*}}, comdat($"??_S?$E@H@@6B@")
+// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@I@@6B@")
+// MSC: @"??_S?$E@H@@6B@" =
+// MSC: @"??_7?$E@I@@6B@" =
 
 extern template struct __declspec(dllimport) C<int>;
 
 extern template struct D<int>;
 
+extern template struct __declspec(dllimport) E<int>;      // $E@H, 1EIiE
+extern template struct E<unsigned>;                       // $E@I, 1EIjE
+extern template struct __declspec(dllimport) E<long int>; // $E@J, 1EIlE
+extern template struct E<unsigned long int>;              // $E@K, 1EImE
+
 void use() {
   C<int> c;
 
@@ -82,6 +126,14 @@ void use() {
   // MSC: call void @"?also_to_be_imported@?$D@H@@QEAAXXZ"
   // GNU: call void @_ZN1DIiE19also_to_be_importedEv
   d.also_to_be_imported(); // implicitly instantiated here
+
+  E<int> ei{1};
+
+  E<unsigned> ej{1};
+
+  E<long int> el{1L};
+
+  E<unsigned long int> eu{1L};
 }
 
 // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
@@ -98,3 +150,20 @@ void use() {
 
 // MSC: define linkonce_odr dso_local void 
@"?also_to_be_imported@?$D@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv
+
+// MSC: declare dllimport noundef ptr @"??0?$E@J@@QEAA@J@Z"
+// MSC: declare dso_local noundef ptr @"??0?$E@K@@QEAA@J@Z"
+// GNU: define linkonce_odr dso_local void @_ZN1EIiEC1Ei
+// GNU: define linkonce_odr dso_local void @_ZN1EIjEC1Ei
+// GNU: declare dllimport void @_ZN1EIlEC1El
+// GNU: declare dso_local void @_ZN1EImEC1El
+// GNU: define linkonce_odr dso_local void @_ZN1EIiEC2Ei
+// GNU: define linkonce_odr dso_local void @_ZN1EIjEC2Ei
+
+// MSC: declare dllimport void @"?to_be_imported@?$E@H@@UEAAXXZ"
+// MSC: declare dso_local void @"?to_be_instantiated@?$E@H@@UEAAXXZ"
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@H@@UEAAXXZ"
+
+// MSC: declare dso_local void @"?to_be_imported@?$E@I@@UEAAXXZ"
+// MSC: declare dso_local void @"?to_be_instantiated@?$E@I@@UEAAXXZ"
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@I@@UEAAXXZ"

>From 5a16b110f4efc41bb1c64d601061d9abff5a472c Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Fri, 5 Dec 2025 21:49:19 +0900
Subject: [PATCH 7/9] fix tests

---
 ...t_instantiation.exclude_from_dllexport.cpp | 46 +++++++++++--------
 ...t_instantiation.exclude_from_dllimport.cpp | 44 +++++++++---------
 2 files changed, 47 insertions(+), 43 deletions(-)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
index 4bff5a828f34c..2f6767bc96142 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp
@@ -40,15 +40,11 @@ template <class T> void C<T>::not_to_be_instantiated() 
noexcept {}
 // Attach the attribute to class template declaration instead of instantiation 
declaration.
 template <class T>
 struct __declspec(dllexport) D {
-  // This should be exported by the class-level attribute.
-  void to_be_exported() noexcept;
-
-  // This also should be exported by the class-level attribute but currently 
not.
-  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_exported() noexcept;
+  // This will be exported if and only if no explicit instantiations are 
provided.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void 
to_be_exported_iff_no_explicit_instantiation() noexcept;
 };
 
-template <class T> void D<T>::to_be_exported() noexcept {}
-template <class T> void D<T>::also_to_be_exported() noexcept {}
+template <class T> void D<T>::to_be_exported_iff_no_explicit_instantiation() 
noexcept {}
 
 // Interaction with VTables.
 template <class T>
@@ -70,23 +66,23 @@ template <class T> void E<T>::to_be_instantiated() noexcept 
{}
 template <class T> void E<T>::to_be_exported_explicitly() noexcept {}
 
 // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
-// MSC: $"?to_be_exported@?$D@H@@QEAAXXZ" = comdat any
 // MSC: $"?to_be_exported@?$E@H@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_exported@?$E@I@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
-// MSC: $"?also_to_be_exported@?$D@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" = 
comdat any
+// MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ" = 
comdat any
 // MSC: $"?to_be_instantiated@?$E@H@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$E@H@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_instantiated@?$E@I@@UEAAXXZ" = comdat any
 // MSC: $"?to_be_exported_explicitly@?$E@I@@UEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
-// GNU: $_ZN1DIiE14to_be_exportedEv = comdat any
 // GNU: $_ZN1EIiE14to_be_exportedEv = comdat any
 // GNU: $_ZN1EIjE14to_be_exportedEv = comdat any
 // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any
-// GNU: $_ZN1DIiE19also_to_be_exportedEv = comdat any
+// GNU: $_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv = comdat any
+// GNU: $_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv = comdat any
 // GNU: $_ZN1EIiE18to_be_instantiatedEv = comdat any
 // GNU: $_ZN1EIiE25to_be_exported_explicitlyEv = comdat any
 // GNU: $_ZN1EIjE18to_be_instantiatedEv = comdat any
@@ -106,10 +102,10 @@ template <class T> void E<T>::to_be_exported_explicitly() 
noexcept {}
 template struct __declspec(dllexport) C<int>;
 
 // MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$D@H@@QEAAAEAU0@AEBU0@@Z"
-// MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported@?$D@H@@QEAAXXZ"
 // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_
-// GNU: define weak_odr dso_local dllexport{{.*}} void 
@_ZN1DIiE14to_be_exportedEv
-template struct D<int>;
+template struct D<int>; // No dllexport here.
+// Don't provide explicit instantiation for D<unsigned>.
+
 
 // MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$E@H@@QEAAAEAU0@AEBU0@@Z"
 // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E@H@@QEAA@XZ"
@@ -127,6 +123,8 @@ template struct __declspec(dllexport) E<int>;
 // GNU: define weak_odr dso_local{{.*}} void @_ZN1EIjE14to_be_exportedEv
 template struct E<unsigned int>;
 
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr 
@"??4?$D@I@@QEAAAEAU0@AEBU0@@Z"
+
 void use() {
   C<int> c;
 
@@ -138,11 +136,17 @@ void use() {
   // GNU: call void @_ZN1CIiE18not_to_be_exportedEv
   c.not_to_be_exported(); // implicitly instantiated here
 
-  D<int> d;
+  D<int> di;
+
+  // MSC: call void 
@"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ"
+  // GNU: call void @_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv
+  di.to_be_exported_iff_no_explicit_instantiation(); // implicitly 
instantiated here
+
+  D<unsigned> dj;
 
-  // MSC: call void @"?also_to_be_exported@?$D@H@@QEAAXXZ"
-  // GNU: call void @_ZN1DIiE19also_to_be_exportedEv
-  d.also_to_be_exported(); // implicitly instantiated here
+  // MSC: call void 
@"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ"
+  // GNU: call void @_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv
+  dj.to_be_exported_iff_no_explicit_instantiation(); // implicitly 
instantiated here
 }
 
 // MSC: define weak_odr dso_local dllexport{{.*}} void 
@"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
@@ -151,8 +155,10 @@ void use() {
 // MSC: define linkonce_odr dso_local void 
@"?not_to_be_exported@?$C@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_exportedEv
 
-// MSC: define linkonce_odr dso_local void 
@"?also_to_be_exported@?$D@H@@QEAAXXZ"
-// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv
+// MSC: define linkonce_odr dso_local void 
@"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void 
@"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void 
@_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv
+// GNU: define weak_odr dso_local dllexport void 
@_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv
 
 // MSC: define linkonce_odr dso_local void 
@"?to_be_instantiated@?$E@H@@UEAAXXZ"
 // MSC: define weak_odr dso_local dllexport void 
@"?to_be_exported_explicitly@?$E@H@@UEAAXXZ"
diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index 6cb169bb5a4d8..ff5298ec56605 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -39,15 +39,11 @@ template <class T> void C<T>::not_to_be_instantiated() 
noexcept {}
 // Attach the attribute to class template declaration instead of instantiation 
declaration.
 template <class T>
 struct __declspec(dllimport) D {
-  // This will be imported by the class-level attribute.
-  void to_be_imported() noexcept;
-
-  // This also should be imported by the class-level attribute but currently 
not.
-  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_imported() noexcept;
+  // This will be imported if and only if no explicit instantiations are 
provided.
+  EXCLUDE_FROM_EXPLICIT_INSTANTIATION void 
to_be_imported_iff_no_explicit_instantiation() noexcept;
 };
 
-template <class T> void D<T>::to_be_imported() noexcept {}
-template <class T> void D<T>::also_to_be_imported() noexcept {}
+template <class T> void D<T>::to_be_imported_iff_no_explicit_instantiation() 
noexcept {}
 
 // Interaction with VTables.
 template <class T>
@@ -82,9 +78,9 @@ template <class T> void E<T>::to_be_imported() noexcept {}
 template <class T> void E<T>::to_be_instantiated() noexcept {}
 
 // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
-// MSC: $"?also_to_be_imported@?$D@H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_imported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" = 
comdat any
 // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
-// GNU: $_ZN1DIiE19also_to_be_importedEv = comdat any
+// GNU: $_ZN1DIiE44to_be_imported_iff_no_explicit_instantiationEv = comdat any
 // GNU: @_ZTV1EIiE = external dllimport unnamed_addr
 // GNU: @_ZTV1EIjE = external unnamed_addr
 
@@ -95,7 +91,8 @@ template <class T> void E<T>::to_be_instantiated() noexcept {}
 
 extern template struct __declspec(dllimport) C<int>;
 
-extern template struct D<int>;
+extern template struct D<int>; // No dllimport here.
+// Don't provide explicit instantiation for D<unsigned>.
 
 extern template struct __declspec(dllimport) E<int>;      // $E@H, 1EIiE
 extern template struct E<unsigned>;                       // $E@I, 1EIjE
@@ -117,15 +114,17 @@ void use() {
   // GNU: call void @_ZN1CIiE18not_to_be_importedEv
   c.not_to_be_imported(); // implicitly instantiated here
 
-  D<int> d;
+  D<int> di;
 
-  // MSC: call void @"?to_be_imported@?$D@H@@QEAAXXZ"
-  // GNU: call void @_ZN1DIiE14to_be_importedEv
-  d.to_be_imported(); // implicitly instantiated here
+  // MSC: call void 
@"?to_be_imported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ"
+  // GNU: call void @_ZN1DIiE44to_be_imported_iff_no_explicit_instantiationEv
+  di.to_be_imported_iff_no_explicit_instantiation(); // implicitly 
instantiated here
 
-  // MSC: call void @"?also_to_be_imported@?$D@H@@QEAAXXZ"
-  // GNU: call void @_ZN1DIiE19also_to_be_importedEv
-  d.also_to_be_imported(); // implicitly instantiated here
+  D<unsigned> dj;
+
+  // MSC: call void 
@"?to_be_imported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ"
+  // GNU: call void @_ZN1DIjE44to_be_imported_iff_no_explicit_instantiationEv
+  dj.to_be_imported_iff_no_explicit_instantiation(); // implicitly 
instantiated here
 
   E<int> ei{1};
 
@@ -133,7 +132,7 @@ void use() {
 
   E<long int> el{1L};
 
-  E<unsigned long int> eu{1L};
+  E<unsigned long int> em{1L};
 }
 
 // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
@@ -145,11 +144,10 @@ void use() {
 // MSC: define linkonce_odr dso_local void 
@"?not_to_be_imported@?$C@H@@QEAAXXZ"
 // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
 
-// MSC: declare dllimport void @"?to_be_imported@?$D@H@@QEAAXXZ"
-// GNU: declare dllimport void @_ZN1DIiE14to_be_importedEv
-
-// MSC: define linkonce_odr dso_local void 
@"?also_to_be_imported@?$D@H@@QEAAXXZ"
-// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv
+// MSC: define linkonce_odr dso_local void 
@"?to_be_imported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ"
+// MSC: declare dllimport void 
@"?to_be_imported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void 
@_ZN1DIiE44to_be_imported_iff_no_explicit_instantiationEv
+// GNU: declare dllimport void 
@_ZN1DIjE44to_be_imported_iff_no_explicit_instantiationEv
 
 // MSC: declare dllimport noundef ptr @"??0?$E@J@@QEAA@J@Z"
 // MSC: declare dso_local noundef ptr @"??0?$E@K@@QEAA@J@Z"

>From 8638ae20fc6bef7b2debfd99f4ca7f8b820d356c Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Fri, 5 Dec 2025 21:49:45 +0900
Subject: [PATCH 8/9] instantiate excluded virtual member functions

---
 clang/lib/Sema/SemaDeclCXX.cpp | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 27dd89fe15ec1..485b2b5ebcb8c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19118,8 +19118,23 @@ bool Sema::DefineUsedVTables() {
         }
       }
 
-      if (IsExplicitInstantiationDeclaration)
+      if (IsExplicitInstantiationDeclaration) {
         DefineVTable = false;
+
+        // Ensure the instance of a virtual member function which is declared
+        // with `__attribute__((exclude_from_explicit_instantiation))` is
+        // accessible from the VTable.
+        for (Decl *decl : Class->decls()) {
+          auto *Method = cast<CXXMethodDecl>(decl);
+          if (!Method || !Method->isVirtual())
+            continue;
+
+          if (Method->hasAttr<ExcludeFromExplicitInstantiationAttr>()) {
+            MarkFunctionReferenced(Loc, Method);
+            DefinedAnything = true;
+          }
+        }
+      }
     }
 
     // The exception specifications for all virtual members may be needed even

>From 2c44ab5066971495a176fae62ac42135506ea7f3 Mon Sep 17 00:00:00 2001
From: kikairoya <[email protected]>
Date: Fri, 5 Dec 2025 21:49:56 +0900
Subject: [PATCH 9/9] update test

---
 ...m_explicit_instantiation.exclude_from_dllimport.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
index ff5298ec56605..5106dd72aa7aa 100644
--- 
a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
+++ 
b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp
@@ -49,8 +49,8 @@ template <class T> void 
D<T>::to_be_imported_iff_no_explicit_instantiation() noe
 template <class T>
 struct E {
   // For the MSVC ABI: this constructor causes implicit instantiation of
-  // the VTable, which should trigger instantiating all virtual member
-  // functions regardless `exclude_from_explicit_instantiation` but currently 
not.
+  // the VTable, which triggers instantiating all virtual member
+  // functions regardless `exclude_from_explicit_instantiation`.
   // For the Itanium ABI: Emitting the VTable is suppressed by implicit
   // instantiation declaration so virtual member functions won't be 
instantiated.
   EXCLUDE_FROM_EXPLICIT_INSTANTIATION explicit E(int);
@@ -79,6 +79,8 @@ template <class T> void E<T>::to_be_instantiated() noexcept {}
 
 // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
 // MSC: $"?to_be_imported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" = 
comdat any
+// MSC: $"?to_be_instantiated@?$E@H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E@I@@UEAAXXZ" = comdat any
 // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
 // GNU: $_ZN1DIiE44to_be_imported_iff_no_explicit_instantiationEv = comdat any
 // GNU: @_ZTV1EIiE = external dllimport unnamed_addr
@@ -159,9 +161,9 @@ void use() {
 // GNU: define linkonce_odr dso_local void @_ZN1EIjEC2Ei
 
 // MSC: declare dllimport void @"?to_be_imported@?$E@H@@UEAAXXZ"
-// MSC: declare dso_local void @"?to_be_instantiated@?$E@H@@UEAAXXZ"
+// MSC: define linkonce_odr dso_local void 
@"?to_be_instantiated@?$E@H@@UEAAXXZ"
 // MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@H@@UEAAXXZ"
 
 // MSC: declare dso_local void @"?to_be_imported@?$E@I@@UEAAXXZ"
-// MSC: declare dso_local void @"?to_be_instantiated@?$E@I@@UEAAXXZ"
+// MSC: define linkonce_odr dso_local void 
@"?to_be_instantiated@?$E@I@@UEAAXXZ"
 // MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@I@@UEAAXXZ"

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

Reply via email to