[clang] [Clang] Allow simpler visibility annotations when targeting win32 and mingw (PR #133699)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 31 04:08:54 PDT 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/133699
MinGW and Win32 disagree on where the `__declspec(dllexport)` should be placed. However, there doesn't
fundamentally seem to be a problem with putting the annotation in both places. This patch adds a new
diagnostic group and `-Wattribute-ignored` warnings about where the attribute is placed if the attribute
is different on the declaration and definition. There is another new warning group
`-Wdllexport-explicit-instantiation` that also diagnoses places where the attribue is technically ignored,
even though the correct place is also annotated. This makes it possible to significantly simplify libc++'s
visibility annotations.
>From e4178604a738d91e1880df968efed63b248520ac Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 30 Mar 2025 14:10:26 +0200
Subject: [PATCH] [Clang] Allow simpler visibility annotations when targeting
win32 and mingw
---
clang/include/clang/Basic/Attr.td | 8 ++++++++
clang/include/clang/Basic/DiagnosticGroups.td | 6 +++++-
.../clang/Basic/DiagnosticSemaKinds.td | 12 ++++++++++-
clang/lib/Sema/SemaDeclCXX.cpp | 5 ++++-
clang/lib/Sema/SemaTemplate.cpp | 20 +++++++++++++++++--
.../dllexport-explicit-instantiation.cpp | 19 ++++++++++++++++++
6 files changed, 65 insertions(+), 5 deletions(-)
create mode 100644 clang/test/SemaCXX/dllexport-explicit-instantiation.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 0999d8065e9f5..6bb8fc05b119a 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4185,6 +4185,14 @@ def DLLExport : InheritableAttr, TargetSpecificAttr<TargetHasDLLImportExport> {
let Documentation = [DLLExportDocs];
}
+def DLLExportOnDecl : InheritableAttr, TargetSpecificAttr<TargetHasDLLImportExport> {
+ // This attribute is only used to warn if there was a `__declspec(dllexport)`
+ // on a declaration, but not on the defintion of an explciit instantiation
+ let Spellings = [];
+ let Subjects = SubjectList<[CXXRecord]>;
+ let Documentation = [InternalOnly];
+}
+
def DLLExportStaticLocal : InheritableAttr, TargetSpecificAttr<TargetHasDLLImportExport> {
// This attribute is used internally only when -fno-dllexport-inlines is
// passed. This attribute is added to inline functions of a class having the
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index b9f08d96151c9..80f27771248bb 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -487,6 +487,9 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment,
ReturnStackAddress]>;
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
+def DllexportExplicitInstantiation :
+ DiagGroup<"dllexport-explicit-instantiation",
+ [DllexportExplicitInstantiationDecl]>;
def ExcessInitializers : DiagGroup<"excess-initializers">;
def ExpansionToDefined : DiagGroup<"expansion-to-defined">;
def FlagEnum : DiagGroup<"flag-enum">;
@@ -870,7 +873,8 @@ def NSReturnsMismatch : DiagGroup<"nsreturns-mismatch">;
def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;
def UnknownAttributes : DiagGroup<"unknown-attributes">;
-def IgnoredAttributes : DiagGroup<"ignored-attributes">;
+def IgnoredAttributes : DiagGroup<"ignored-attributes",
+ [DllexportExplicitInstantiation]>;
def Attributes : DiagGroup<"attributes", [UnknownAttributes,
IgnoredAttributes]>;
def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1e900437d41ce..31324b6b1262f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3736,9 +3736,19 @@ def warn_attribute_dllimport_static_field_definition : Warning<
def warn_attribute_dllexport_explicit_instantiation_decl : Warning<
"explicit instantiation declaration should not be 'dllexport'">,
InGroup<DllexportExplicitInstantiationDecl>;
-def warn_attribute_dllexport_explicit_instantiation_def : Warning<
+def warn_attr_dllexport_explicit_inst_def : Warning<
"'dllexport' attribute ignored on explicit instantiation definition">,
+ InGroup<DllexportExplicitInstantiation>;
+def warn_attr_dllexport_explicit_inst_def_mismatch : Warning<
+ "'dllexport' attribute ignored on explicit instantiation definition">,
+ InGroup<IgnoredAttributes>;
+def note_prev_decl_missing_dllexport : Note<
+ "'dllexport' attribute is missing on previous declaration">;
+def warn_dllexport_on_decl_ignored : Warning<
+ "explicit instantiation definition is not exported without 'dllexport'">,
InGroup<IgnoredAttributes>;
+def note_dllexport_on_decl : Note<
+ "'dllexport' attribute on the declaration is ignored">;
def warn_attribute_exclude_from_explicit_instantiation_local_class : Warning<
"%0 attribute ignored on local class%select{| member}1">,
InGroup<IgnoredAttributes>;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 676d53a1f4b45..4d69c11678620 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6606,7 +6606,10 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
if (ClassExported && !ClassAttr->isInherited() &&
TSK == TSK_ExplicitInstantiationDeclaration &&
!Context.getTargetInfo().getTriple().isWindowsGNUEnvironment()) {
- Class->dropAttr<DLLExportAttr>();
+ if (auto *DEA = Class->getAttr<DLLExportAttr>()) {
+ Class->addAttr(DLLExportOnDeclAttr::Create(Context, DEA->getLoc()));
+ Class->dropAttr<DLLExportAttr>();
+ }
return;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index be81b6a46b2c0..25d84fde65970 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9845,13 +9845,29 @@ DeclResult Sema::ActOnExplicitInstantiation(
// mode, if a previous declaration of the instantiation was seen.
for (const ParsedAttr &AL : Attr) {
if (AL.getKind() == ParsedAttr::AT_DLLExport) {
- Diag(AL.getLoc(),
- diag::warn_attribute_dllexport_explicit_instantiation_def);
+ if (PrevDecl->hasAttr<DLLExportAttr>()) {
+ Diag(AL.getLoc(), diag::warn_attr_dllexport_explicit_inst_def);
+ } else {
+ Diag(AL.getLoc(),
+ diag::warn_attr_dllexport_explicit_inst_def_mismatch);
+ Diag(PrevDecl->getLocation(), diag::note_prev_decl_missing_dllexport);
+ }
break;
}
}
}
+ if (TSK == TSK_ExplicitInstantiationDefinition && PrevDecl &&
+ !Context.getTargetInfo().getTriple().isWindowsGNUEnvironment() &&
+ llvm::none_of(Attr, [](const ParsedAttr &AL) {
+ return AL.getKind() == ParsedAttr::AT_DLLExport;
+ })) {
+ if (const auto *DEA = PrevDecl->getAttr<DLLExportOnDeclAttr>()) {
+ Diag(TemplateLoc, diag::warn_dllexport_on_decl_ignored);
+ Diag(DEA->getLoc(), diag::note_dllexport_on_decl);
+ }
+ }
+
if (CheckExplicitInstantiation(*this, ClassTemplate, TemplateNameLoc,
SS.isSet(), TSK))
return true;
diff --git a/clang/test/SemaCXX/dllexport-explicit-instantiation.cpp b/clang/test/SemaCXX/dllexport-explicit-instantiation.cpp
new file mode 100644
index 0000000000000..522e81d37976d
--- /dev/null
+++ b/clang/test/SemaCXX/dllexport-explicit-instantiation.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-win32 -fsyntax-only -fms-extensions -verify=win32,win32-pedantic -DMS %s -Wdllexport-explicit-instantiation
+// RUN: %clang_cc1 -triple x86_64-win32 -fsyntax-only -fms-extensions -verify=win32 -DMS %s -Wno-dllexport-explicit-instantiation
+// RUN: %clang_cc1 -triple x86_64-mingw32 -fsyntax-only -fms-extensions -verify=mingw,mingw-pedantic -DMS %s -Wdllexport-explicit-instantiation
+// RUN: %clang_cc1 -triple x86_64-mingw32 -fsyntax-only -fms-extensions -verify=mingw -DMS %s -Wno-dllexport-explicit-instantiation
+
+template <class>
+class S {};
+
+extern template class __declspec(dllexport) S<short>; // win32-pedantic-warning {{explicit instantiation declaration should not be 'dllexport'}} \
+ win32-pedantic-note {{attribute is here}}
+template class __declspec(dllexport) S<short>; // mingw-pedantic-warning {{'dllexport' attribute ignored on explicit instantiation definition}}
+
+extern template class __declspec(dllexport) S<int>; // win32-pedantic-warning {{explicit instantiation declaration should not be 'dllexport'}} \
+ win32-pedantic-note {{attribute is here}} \
+ win32-note {{'dllexport' attribute on the declaration is ignored}}
+template class S<int>; // win32-warning {{explicit instantiation definition is not exported without 'dllexport'}}
+
+extern template class S<long>; // mingw-note {{'dllexport' attribute is missing on previous declaration}}
+template class __declspec(dllexport) S<long>; // mingw-warning {{'dllexport' attribute ignored on explicit instantiation definition}}
More information about the cfe-commits
mailing list