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/6] 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/6] [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/6] 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/6] 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/6] 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/6] 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"

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

Reply via email to