[clang] [Clang] Apply exclude_from_explicit_instantiation to dllimport/dllexport (PR #168171)
Tomohiro Kashiwada via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 21 04:34:16 PST 2026
https://github.com/kikairoya updated https://github.com/llvm/llvm-project/pull/168171
>From 3234c2685a86561a3df695f6a8e4b6c55c1a7f5c Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Sat, 15 Nov 2025 11:25:23 +0900
Subject: [PATCH 01/16] 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 at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$C at H@@QEAAXXZ" = comdat any
+// MSC: $"?not_to_be_exported@?$C at H@@QEAAXXZ" = comdat any
+// MSC: $"?not_to_be_instantiated@?$C at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv
+
+// MSC: declare dllimport void @"?not_to_be_imported@?$C at H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv
>From 1e558daf82c6750b14b6ea273175d29279cce450 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Sat, 15 Nov 2025 11:25:24 +0900
Subject: [PATCH 02/16] [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 <kikairoya at gmail.com>
Date: Sat, 15 Nov 2025 11:25:24 +0900
Subject: [PATCH 03/16] 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 at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?not_to_be_exported@?$C at H@@QEAAXXZ" = comdat any
-// MSC: $"?not_to_be_instantiated@?$C at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE18not_to_be_importedEv
@@ -46,5 +51,8 @@ void use() {
// MSC: declare dllimport void @"?to_be_imported@?$C at H@@QEAAXXZ"
// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv
-// MSC: declare dllimport void @"?not_to_be_imported@?$C at H@@QEAAXXZ"
-// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$C at H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1CIiE25to_be_imported_explicitlyEv
+
+// MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C at H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
>From 94d7cf7933a73410548dc787baf6d9eac5b0d60f Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Sun, 30 Nov 2025 10:03:12 +0900
Subject: [PATCH 04/16] 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 at H@@QEAAAEAU0 at AEBU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C at 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 at 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 at 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 at 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 at H@@QEAAXXZ"
// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv
>From bdbeeac2ccefd653641ab115edf58408d8c12d91 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Mon, 1 Dec 2025 21:15:38 +0900
Subject: [PATCH 05/16] 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 at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$D at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?not_to_be_exported@?$C at H@@QEAAXXZ" = comdat any
+// MSC: $"?also_to_be_exported@?$D at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$D at 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 at 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 at 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 at H@@QEAAXXZ"
@@ -67,3 +96,6 @@ void use() {
// MSC: define linkonce_odr dso_local void @"?not_to_be_exported@?$C at 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 at 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 at H@@QEAAXXZ" = comdat any
+// MSC: $"?also_to_be_imported@?$D at 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 at 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 at H@@QEAAXXZ"
+ // GNU: call void @_ZN1DIiE14to_be_importedEv
+ d.to_be_imported(); // implicitly instantiated here
+
+ // MSC: call void @"?also_to_be_imported@?$D at 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 at H@@QEAAXXZ"
@@ -64,3 +92,9 @@ void use() {
// MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C at H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
+
+// MSC: declare dllimport void @"?to_be_imported@?$D at H@@QEAAXXZ"
+// GNU: declare dllimport void @_ZN1DIiE14to_be_importedEv
+
+// MSC: define linkonce_odr dso_local void @"?also_to_be_imported@?$D at H@@QEAAXXZ"
+// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv
>From bf8df954888217b07b5c5f99f0b98cb5d58e0e6d Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Sun, 30 Nov 2025 07:37:12 +0900
Subject: [PATCH 06/16] 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 at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$E at H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported@?$E at I@@UEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?not_to_be_exported@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?also_to_be_exported@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E at H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$E at H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E at I@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_explicitly@?$E at 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 at H@@6B@")
+// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E at I@@6B@")
+// MSC: @"??_7?$E at H@@6B@" = dllexport unnamed_addr
+// MSC: @"??_7?$E at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA at XZ"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$E at 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 at 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 at H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv
+
+// MSC: define linkonce_odr dso_local void @"?to_be_instantiated@?$E at H@@UEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void @"?to_be_exported_explicitly@?$E at 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 at I@@UEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void @"?to_be_exported_explicitly@?$E at 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 at H@@QEAAXXZ" = comdat any
// MSC: $"?also_to_be_imported@?$D at 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 at H@@6B@")
+// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E at I@@6B@")
+// MSC: @"??_S?$E at H@@6B@" =
+// MSC: @"??_7?$E at I@@6B@" =
extern template struct __declspec(dllimport) C<int>;
extern template struct D<int>;
+extern template struct __declspec(dllimport) E<int>; // $E at H, 1EIiE
+extern template struct E<unsigned>; // $E at I, 1EIjE
+extern template struct __declspec(dllimport) E<long int>; // $E at J, 1EIlE
+extern template struct E<unsigned long int>; // $E at K, 1EImE
+
void use() {
C<int> c;
@@ -82,6 +126,14 @@ void use() {
// MSC: call void @"?also_to_be_imported@?$D at 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 at H@@QEAAXXZ"
@@ -98,3 +150,20 @@ void use() {
// MSC: define linkonce_odr dso_local void @"?also_to_be_imported@?$D at H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv
+
+// MSC: declare dllimport noundef ptr @"??0?$E at J@@QEAA at J@Z"
+// MSC: declare dso_local noundef ptr @"??0?$E at K@@QEAA at 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 at H@@UEAAXXZ"
+// MSC: declare dso_local void @"?to_be_instantiated@?$E at H@@UEAAXXZ"
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E at H@@UEAAXXZ"
+
+// MSC: declare dso_local void @"?to_be_imported@?$E at I@@UEAAXXZ"
+// MSC: declare dso_local void @"?to_be_instantiated@?$E at I@@UEAAXXZ"
+// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E at I@@UEAAXXZ"
>From 5a16b110f4efc41bb1c64d601061d9abff5a472c Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Fri, 5 Dec 2025 21:49:19 +0900
Subject: [PATCH 07/16] 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 at H@@QEAAXXZ" = comdat any
-// MSC: $"?to_be_exported@?$D at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported@?$E at H@@UEAAXXZ" = comdat any
// MSC: $"?to_be_exported@?$E at I@@UEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?not_to_be_exported@?$C at H@@QEAAXXZ" = comdat any
-// MSC: $"?also_to_be_exported@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D at I@@QEAAXXZ" = comdat any
// MSC: $"?to_be_instantiated@?$E at H@@UEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$E at H@@UEAAXXZ" = comdat any
// MSC: $"?to_be_instantiated@?$E at I@@UEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$E at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
-// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$D at 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 at H@@QEAAAEAU0 at AEBU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA at 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 at I@@QEAAAEAU0 at 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 at 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 at 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 at 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 at H@@QEAAXXZ"
@@ -151,8 +155,10 @@ void use() {
// MSC: define linkonce_odr dso_local void @"?not_to_be_exported@?$C at 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 at 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 at H@@QEAAXXZ"
+// MSC: define weak_odr dso_local dllexport void @"?to_be_exported_iff_no_explicit_instantiation@?$D at 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 at H@@UEAAXXZ"
// MSC: define weak_odr dso_local dllexport void @"?to_be_exported_explicitly@?$E at 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 at H@@QEAAXXZ" = comdat any
-// MSC: $"?also_to_be_imported@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_imported_iff_no_explicit_instantiation@?$D at 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 at H, 1EIiE
extern template struct E<unsigned>; // $E at 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 at 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 at 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 at 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 at 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 at H@@QEAAXXZ"
@@ -145,11 +144,10 @@ void use() {
// MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C at H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
-// MSC: declare dllimport void @"?to_be_imported@?$D at H@@QEAAXXZ"
-// GNU: declare dllimport void @_ZN1DIiE14to_be_importedEv
-
-// MSC: define linkonce_odr dso_local void @"?also_to_be_imported@?$D at 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 at H@@QEAAXXZ"
+// MSC: declare dllimport void @"?to_be_imported_iff_no_explicit_instantiation@?$D at 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 at J@@QEAA at J@Z"
// MSC: declare dso_local noundef ptr @"??0?$E at K@@QEAA at J@Z"
>From d40c7412e780977bd7830704a17a9e32f1b075d9 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Fri, 5 Dec 2025 21:49:45 +0900
Subject: [PATCH 08/16] 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..77bd30599e1bf 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 = dyn_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 73b9cf48eec841a6a13b89141cd606a92ab1a0c7 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Fri, 5 Dec 2025 21:49:56 +0900
Subject: [PATCH 09/16] 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 at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_imported_iff_no_explicit_instantiation@?$D at H@@QEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E at H@@UEAAXXZ" = comdat any
+// MSC: $"?to_be_instantiated@?$E at 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 at H@@UEAAXXZ"
-// MSC: declare dso_local void @"?to_be_instantiated@?$E at H@@UEAAXXZ"
+// MSC: define linkonce_odr dso_local void @"?to_be_instantiated@?$E at H@@UEAAXXZ"
// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E at H@@UEAAXXZ"
// MSC: declare dso_local void @"?to_be_imported@?$E at I@@UEAAXXZ"
-// MSC: declare dso_local void @"?to_be_instantiated@?$E at I@@UEAAXXZ"
+// MSC: define linkonce_odr dso_local void @"?to_be_instantiated@?$E at I@@UEAAXXZ"
// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E at I@@UEAAXXZ"
>From f2f249690fec9b8245dc17cdcfc605fd632a2fff Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 10 Dec 2025 20:58:20 +0900
Subject: [PATCH 10/16] post-merge fix
---
..._explicit_instantiation.exclude_from_dllexport.cpp | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
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 2f6767bc96142..369d505a2f6b4 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
@@ -96,26 +96,34 @@ template <class T> void E<T>::to_be_exported_explicitly() noexcept {}
// GNU:@_ZTV1EIjE = weak_odr dso_local unnamed_addr constant {{.*}}, comdat
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$C at H@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$C at H@@QEAAAEAU0@$$QEAU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C at H@@QEAAXXZ"
// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1CIiEaSERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1CIiEaSEOS0_
// 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 at H@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D at H@@QEAAAEAU0@$$QEAU0@@Z"
// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSEOS0_
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 at H@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$E at H@@QEAAAEAU0@$$QEAU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA at XZ"
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E at H@@QEAA@$$QEAU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$E at H@@UEAAXXZ"
// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1EIiEaSERKS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1EIiEaSEOS0_
// 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 @_ZN1EIiEC2EOS0_
+// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1EOS0_
// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiE14to_be_exportedEv
template struct __declspec(dllexport) E<int>;
@@ -124,6 +132,7 @@ template struct __declspec(dllexport) E<int>;
template struct E<unsigned int>;
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D at I@@QEAAAEAU0 at AEBU0@@Z"
+// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D at I@@QEAAAEAU0@$$QEAU0@@Z"
void use() {
C<int> c;
>From 69378a59b88f3324c137732632ae4a1c7e8d4e7a Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 10 Dec 2025 20:58:36 +0900
Subject: [PATCH 11/16] drop noexcept from tests
---
...t_instantiation.exclude_from_dllexport.cpp | 32 +++++++++----------
...t_instantiation.exclude_from_dllimport.cpp | 28 ++++++++--------
2 files changed, 30 insertions(+), 30 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 369d505a2f6b4..c955c932a3619 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
@@ -19,51 +19,51 @@ 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;
+ void to_be_exported();
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void to_be_exported_explicitly();
// This will be instantiated implicitly but won't be exported.
- EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported() noexcept;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported();
// This won't be instantiated.
- EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated();
};
-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 {}
+template <class T> void C<T>::to_be_exported() {}
+template <class T> void C<T>::to_be_exported_explicitly() {}
+template <class T> void C<T>::not_to_be_exported() {}
+template <class T> void C<T>::not_to_be_instantiated() {}
// Attach the attribute to class template declaration instead of instantiation declaration.
template <class T>
struct __declspec(dllexport) D {
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void to_be_exported_iff_no_explicit_instantiation();
};
-template <class T> void D<T>::to_be_exported_iff_no_explicit_instantiation() noexcept {}
+template <class T> void D<T>::to_be_exported_iff_no_explicit_instantiation() {}
// Interaction with VTables.
template <class T>
struct E {
// This will be instanciated by the explicit template instantiation definition.
- virtual void to_be_exported() noexcept;
+ virtual void to_be_exported();
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated();
// This too, but will be exported by the member attribute.
- EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) virtual void to_be_exported_explicitly() noexcept;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) virtual void to_be_exported_explicitly();
};
-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 {}
+template <class T> void E<T>::to_be_exported() {}
+template <class T> void E<T>::to_be_instantiated() {}
+template <class T> void E<T>::to_be_exported_explicitly() {}
// MSC: $"?to_be_exported@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported@?$E at H@@UEAAXXZ" = comdat any
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 5106dd72aa7aa..492a83767c7e5 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
@@ -19,31 +19,31 @@ 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;
+ void to_be_imported();
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void to_be_imported_explicitly();
// This will be instantiated implicitly but won't be imported.
- EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported();
// This won't be instantiated.
- EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated();
};
-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 {}
+template <class T> void C<T>::to_be_imported() {}
+template <class T> void C<T>::not_to_be_imported() {}
+template <class T> void C<T>::not_to_be_instantiated() {}
// Attach the attribute to class template declaration instead of instantiation declaration.
template <class T>
struct __declspec(dllimport) D {
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void to_be_imported_iff_no_explicit_instantiation();
};
-template <class T> void D<T>::to_be_imported_iff_no_explicit_instantiation() noexcept {}
+template <class T> void D<T>::to_be_imported_iff_no_explicit_instantiation() {}
// Interaction with VTables.
template <class T>
@@ -61,21 +61,21 @@ struct E {
// The body of this shouldn't be emitted since instantiation is suppressed
// by the explicit instantiation declaration.
- virtual void to_be_imported() noexcept;
+ virtual void to_be_imported();
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated();
// 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;
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) virtual void to_be_imported_explicitly();
};
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 {}
+template <class T> void E<T>::to_be_imported() {}
+template <class T> void E<T>::to_be_instantiated() {}
// MSC: $"?not_to_be_imported@?$C at H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_imported_iff_no_explicit_instantiation@?$D at H@@QEAAXXZ" = comdat any
>From 7d9eb14e8e255fe3942b8b598bc52cabab6cea1e Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 10 Dec 2025 20:58:40 +0900
Subject: [PATCH 12/16] revise logic
---
clang/lib/Sema/SemaDeclCXX.cpp | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3ac8011c030bc..1a470c7dd3b0e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19119,23 +19119,18 @@ bool Sema::DefineUsedVTables() {
}
}
- if (IsExplicitInstantiationDeclaration) {
+ if (IsExplicitInstantiationDeclaration &&
+ llvm::none_of(Class->decls(), [](Decl *decl) {
+ // If the class has a virtual member function declared with
+ // `__attribute__((exclude_from_explicit_instantiation))`, the
+ // explicit instantiation declaration shouldn't suppress emitting
+ // the vtable to ensure that the excluded member function is
+ // accessible through the vtable.
+ auto *Method = dyn_cast<CXXMethodDecl>(decl);
+ return Method && Method->isVirtual() &&
+ Method->hasAttr<ExcludeFromExplicitInstantiationAttr>();
+ }))
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 = dyn_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 6e487588dfca444d0f3ee2315bc5fc49cad1534d Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Thu, 25 Dec 2025 21:19:37 +0900
Subject: [PATCH 13/16] store to a named variable
---
clang/lib/Sema/SemaDeclCXX.cpp | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1a470c7dd3b0e..dc46e50171c93 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19119,18 +19119,21 @@ bool Sema::DefineUsedVTables() {
}
}
- if (IsExplicitInstantiationDeclaration &&
- llvm::none_of(Class->decls(), [](Decl *decl) {
- // If the class has a virtual member function declared with
- // `__attribute__((exclude_from_explicit_instantiation))`, the
- // explicit instantiation declaration shouldn't suppress emitting
- // the vtable to ensure that the excluded member function is
- // accessible through the vtable.
- auto *Method = dyn_cast<CXXMethodDecl>(decl);
- return Method && Method->isVirtual() &&
- Method->hasAttr<ExcludeFromExplicitInstantiationAttr>();
- }))
- DefineVTable = false;
+ if (IsExplicitInstantiationDeclaration) {
+ const bool HasExcludeFromExplicitInstantiation =
+ llvm::any_of(Class->decls(), [](Decl *decl) {
+ // If the class has a virtual member function declared with
+ // `__attribute__((exclude_from_explicit_instantiation))`, the
+ // explicit instantiation declaration shouldn't suppress emitting
+ // the vtable to ensure that the excluded member function is
+ // accessible through the vtable.
+ auto *Method = dyn_cast<CXXMethodDecl>(decl);
+ return Method && Method->isVirtual() &&
+ Method->hasAttr<ExcludeFromExplicitInstantiationAttr>();
+ });
+ if (!HasExcludeFromExplicitInstantiation)
+ DefineVTable = false;
+ }
}
// The exception specifications for all virtual members may be needed even
>From d9c78ee6f3d1149484e95b0111e6847dae3a06c0 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Thu, 25 Dec 2025 21:16:19 +0900
Subject: [PATCH 14/16] add a comment to explain silencing implicit checks
---
...xclude_from_explicit_instantiation.exclude_from_dllexport.cpp | 1 +
...xclude_from_explicit_instantiation.exclude_from_dllimport.cpp | 1 +
2 files changed, 2 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 c955c932a3619..7a9d1343f451e 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
@@ -8,6 +8,7 @@
// Test that __declspec(dllexport) doesn't instantiate entities marked with
// the exclude_from_explicit_instantiation attribute unless marked as dllexport explicitly.
+// Silence --implicit-check-not=dllexport.
// MSC: ModuleID = {{.*}}exclude_from_dllexport.cpp
// MSC: source_filename = {{.*}}exclude_from_dllexport.cpp
// GNU: ModuleID = {{.*}}exclude_from_dllexport.cpp
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 492a83767c7e5..353e7f8facc3b 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
@@ -8,6 +8,7 @@
// Test that __declspec(dllimport) doesn't instantiate entities marked with
// the exclude_from_explicit_instantiation attribute unless marked as dllimport explicitly.
+// Silence --implicit-check-not=dllimport.
// MSC: ModuleID = {{.*}}exclude_from_dllimport.cpp
// MSC: source_filename = {{.*}}exclude_from_dllimport.cpp
// GNU: ModuleID = {{.*}}exclude_from_dllimport.cpp
>From a8a745952993fcb4fd7495fb263ea2d073947b45 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 21 Jan 2026 21:03:54 +0900
Subject: [PATCH 15/16] nit
---
...ude_from_explicit_instantiation.exclude_from_dllexport.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 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 7a9d1343f451e..d5c942c2d82d6 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
@@ -93,8 +93,8 @@ template <class T> void E<T>::to_be_exported_explicitly() {}
// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E at I@@6B@")
// MSC: @"??_7?$E at H@@6B@" = dllexport unnamed_addr
// MSC: @"??_7?$E at 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
+// 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 at H@@QEAAAEAU0 at AEBU0@@Z"
// MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$C at H@@QEAAAEAU0@$$QEAU0@@Z"
>From 8f6b530c5b6d7e0d8a23ff27ab85faff38aecddf Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 21 Jan 2026 21:31:59 +0900
Subject: [PATCH 16/16] drop isVirtual
---
clang/lib/Sema/SemaDeclCXX.cpp | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index dc46e50171c93..df381736f24ce 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19121,15 +19121,14 @@ bool Sema::DefineUsedVTables() {
if (IsExplicitInstantiationDeclaration) {
const bool HasExcludeFromExplicitInstantiation =
- llvm::any_of(Class->decls(), [](Decl *decl) {
- // If the class has a virtual member function declared with
+ llvm::any_of(Class->methods(), [](CXXMethodDecl *method) {
+ // If the class has a member function declared with
// `__attribute__((exclude_from_explicit_instantiation))`, the
- // explicit instantiation declaration shouldn't suppress emitting
- // the vtable to ensure that the excluded member function is
- // accessible through the vtable.
- auto *Method = dyn_cast<CXXMethodDecl>(decl);
- return Method && Method->isVirtual() &&
- Method->hasAttr<ExcludeFromExplicitInstantiationAttr>();
+ // explicit instantiation declaration should not suppress emitting
+ // the vtable, since the corresponding explicit instantiation
+ // definition might not emit the vtable if a triggering method is
+ // excluded.
+ return method->hasAttr<ExcludeFromExplicitInstantiationAttr>();
});
if (!HasExcludeFromExplicitInstantiation)
DefineVTable = false;
More information about the cfe-commits
mailing list