This revision was automatically updated to reflect the committed changes.
Closed by commit rGc9b771b9fc2f: Keep inherited dllimport/export attrs for
explicit specialization of class… (authored by hans).
Changed prior to commit:
https://reviews.llvm.org/D135154?vs=464984&id=466023#toc
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D135154/new/
https://reviews.llvm.org/D135154
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/test/CodeGenCXX/dllexport-members.cpp
clang/test/CodeGenCXX/dllimport-members.cpp
clang/test/SemaCXX/dllexport.cpp
clang/test/SemaCXX/dllimport.cpp
Index: clang/test/SemaCXX/dllimport.cpp
===================================================================
--- clang/test/SemaCXX/dllimport.cpp
+++ clang/test/SemaCXX/dllimport.cpp
@@ -1138,6 +1138,9 @@
// Import individual members of a class template.
template<typename T>
struct ImportClassTmplMembers {
+#ifndef GNU
+// expected-note@+2{{attribute is here}}
+#endif
__declspec(dllimport) void normalDecl();
#ifdef GNU
// expected-note@+2{{previous attribute is here}}
@@ -1300,6 +1303,34 @@
template<typename T> __declspec(dllimport) constexpr int CTMR<T>::ConstexprField;
+// MSVC imports explicit specialization of imported class template member
+// function, and errors on such definitions. MinGW does not treat them as
+// dllimport.
+template <typename> struct ClassTmpl {
+#if !defined(GNU)
+// expected-note@+2{{attribute is here}}
+#endif
+ void __declspec(dllimport) importedNormal();
+#if !defined(GNU)
+// expected-note@+2{{attribute is here}}
+#endif
+ static void __declspec(dllimport) importedStatic();
+};
+#if !defined(GNU)
+// expected-error@+2{{cannot define non-inline dllimport template specialization}}
+#endif
+template<> void ClassTmpl<int>::importedNormal() {}
+#if !defined(GNU)
+// expected-error@+2{{cannot define non-inline dllimport template specialization}}
+#endif
+template<> void ClassTmpl<int>::importedStatic() {}
+
+#if !defined(GNU)
+// expected-error@+3{{cannot define non-inline dllimport template specialization}}
+// expected-error@+2{{attribute 'dllimport' cannot be applied to a deleted function}}
+#endif
+template <> void ImportClassTmplMembers<int>::normalDecl() = delete;
+
//===----------------------------------------------------------------------===//
// Class template member templates
Index: clang/test/SemaCXX/dllexport.cpp
===================================================================
--- clang/test/SemaCXX/dllexport.cpp
+++ clang/test/SemaCXX/dllexport.cpp
@@ -1,11 +1,11 @@
-// RUN: %clang_cc1 -triple i686-win32 -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template -DMS %s
-// RUN: %clang_cc1 -triple x86_64-win32 -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template -DMS %s
-// RUN: %clang_cc1 -triple i686-mingw32 -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template %s
-// RUN: %clang_cc1 -triple x86_64-mingw32 -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template %s
-// RUN: %clang_cc1 -triple i686-windows-itanium -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template -DWI %s
-// RUN: %clang_cc1 -triple x86_64-windows-itanium -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template -DWI %s
-// RUN: %clang_cc1 -triple x86_64-scei-ps4 -fsyntax-only -fdeclspec -verify -std=c++11 -Wunsupported-dll-base-class-template -DWI %s
-// RUN: %clang_cc1 -triple x86_64-sie-ps5 -fsyntax-only -fdeclspec -verify -std=c++1y -Wunsupported-dll-base-class-template -DWI %s
+// RUN: %clang_cc1 -triple i686-win32 -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template -DMS %s
+// RUN: %clang_cc1 -triple x86_64-win32 -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template -DMS %s
+// RUN: %clang_cc1 -triple i686-mingw32 -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template -DGNU %s
+// RUN: %clang_cc1 -triple x86_64-mingw32 -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template -DGNU %s
+// RUN: %clang_cc1 -triple i686-windows-itanium -fsyntax-only -fms-extensions -verify -std=c++11 -Wunsupported-dll-base-class-template -DWI %s
+// RUN: %clang_cc1 -triple x86_64-windows-itanium -fsyntax-only -fms-extensions -verify -std=c++1y -Wunsupported-dll-base-class-template -DWI %s
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -fsyntax-only -fdeclspec -verify -std=c++11 -Wunsupported-dll-base-class-template -DWI %s
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -fsyntax-only -fdeclspec -verify -std=c++1y -Wunsupported-dll-base-class-template -DWI %s
// Helper structs to make templates more expressive.
struct ImplicitInst_Exported {};
@@ -1087,6 +1087,13 @@
#endif
template<typename T> __declspec(dllexport) constexpr int CTMR<T>::ConstexprField;
+// MSVC exports explicit specialization of exported class template member
+// function, and errors on such definitions. MinGW does not treat them as
+// dllexport.
+#if !defined(GNU)
+// expected-error@+2{{attribute 'dllexport' cannot be applied to a deleted function}}
+#endif
+template <> void ExportClassTmplMembers<int>::normalDecl() = delete;
//===----------------------------------------------------------------------===//
Index: clang/test/CodeGenCXX/dllimport-members.cpp
===================================================================
--- clang/test/CodeGenCXX/dllimport-members.cpp
+++ clang/test/CodeGenCXX/dllimport-members.cpp
@@ -875,3 +875,23 @@
// GNU-DAG: @_ZN10MemVarTmpl9StaticVarI21ExplicitSpec_ImportedEE = external dllimport constant i32
template<> __declspec(dllimport) const int MemVarTmpl::StaticVar<ExplicitSpec_Imported>;
USEMV(MemVarTmpl, StaticVar<ExplicitSpec_Imported>)
+
+
+//===----------------------------------------------------------------------===//
+// Class template members
+//===----------------------------------------------------------------------===//
+
+template <typename> struct ClassTmplMem {
+ void __declspec(dllimport) importedNormal();
+ static void __declspec(dllimport) importedStatic();
+};
+// MSVC imports explicit specialization of imported class template member function; MinGW does not.
+// M32-DAG: declare dllimport x86_thiscallcc void @"?importedNormal@?$ClassTmplMem@H@@QAEXXZ"
+// G32-DAG: declare dso_local x86_thiscallcc void @_ZN12ClassTmplMemIiE14importedNormalEv
+template<> void ClassTmplMem<int>::importedNormal();
+USEMF(ClassTmplMem<int>, importedNormal);
+
+// M32-DAG: declare dllimport void @"?importedStatic@?$ClassTmplMem@H@@SAXXZ"
+// G32-DAG: declare dso_local void @_ZN12ClassTmplMemIiE14importedStaticEv
+template<> void ClassTmplMem<int>::importedStatic();
+USEMF(ClassTmplMem<int>, importedStatic);
Index: clang/test/CodeGenCXX/dllexport-members.cpp
===================================================================
--- clang/test/CodeGenCXX/dllexport-members.cpp
+++ clang/test/CodeGenCXX/dllexport-members.cpp
@@ -679,3 +679,21 @@
// MSC-DAG: @"??$StaticVar@UExplicitSpec_Def_Exported@@@MemVarTmpl@@2HB" = weak_odr dso_local dllexport constant i32 1, comdat, align 4
// GNU-DAG: @_ZN10MemVarTmpl9StaticVarI25ExplicitSpec_Def_ExportedEE = dso_local dllexport constant i32 1, align 4
template<> __declspec(dllexport) const int MemVarTmpl::StaticVar<ExplicitSpec_Def_Exported> = 1;
+
+
+//===----------------------------------------------------------------------===//
+// Class template members
+//===----------------------------------------------------------------------===//
+
+template <typename> struct ClassTmplMem {
+ void __declspec(dllexport) exportedNormal();
+ static void __declspec(dllexport) exportedStatic();
+};
+// MSVC exports explicit specialization of exported class template member function; MinGW does not.
+// M32-DAG: define dso_local dllexport x86_thiscallcc void @"?exportedNormal@?$ClassTmplMem@H@@QAEXXZ"
+// G32-DAG: define dso_local x86_thiscallcc void @_ZN12ClassTmplMemIiE14exportedNormalEv
+template<> void ClassTmplMem<int>::exportedNormal() {}
+
+// M32-DAG: define dso_local dllexport void @"?exportedStatic@?$ClassTmplMem@H@@SAXXZ"
+// G32-DAG: define dso_local void @_ZN12ClassTmplMemIiE14exportedStaticEv
+template<> void ClassTmplMem<int>::exportedStatic() {}
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -8919,9 +8919,12 @@
/// \brief Strips various properties off an implicit instantiation
/// that has just been explicitly specialized.
-static void StripImplicitInstantiation(NamedDecl *D) {
- D->dropAttr<DLLImportAttr>();
- D->dropAttr<DLLExportAttr>();
+static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) {
+ if (MinGW || (isa<FunctionDecl>(D) &&
+ cast<FunctionDecl>(D)->isFunctionTemplateSpecialization())) {
+ D->dropAttr<DLLImportAttr>();
+ D->dropAttr<DLLExportAttr>();
+ }
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
FD->setInlineSpecified(false);
@@ -8996,7 +8999,9 @@
if (PrevPointOfInstantiation.isInvalid()) {
// The declaration itself has not actually been instantiated, so it is
// still okay to specialize it.
- StripImplicitInstantiation(PrevDecl);
+ StripImplicitInstantiation(
+ PrevDecl,
+ Context.getTargetInfo().getTriple().isWindowsGNUEnvironment());
return false;
}
// Fall through
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -7041,13 +7041,24 @@
(!IsInline || (IsMicrosoftABI && IsTemplate)) && !IsStaticDataMember &&
!NewDecl->isLocalExternDecl() && !IsQualifiedFriend) {
if (IsMicrosoftABI && IsDefinition) {
- S.Diag(NewDecl->getLocation(),
- diag::warn_redeclaration_without_import_attribute)
- << NewDecl;
- S.Diag(OldDecl->getLocation(), diag::note_previous_declaration);
- NewDecl->dropAttr<DLLImportAttr>();
- NewDecl->addAttr(
- DLLExportAttr::CreateImplicit(S.Context, NewImportAttr->getRange()));
+ if (IsSpecialization) {
+ S.Diag(
+ NewDecl->getLocation(),
+ diag::err_attribute_dllimport_function_specialization_definition);
+ S.Diag(OldImportAttr->getLocation(), diag::note_attribute);
+ NewDecl->dropAttr<DLLImportAttr>();
+ } else {
+ S.Diag(NewDecl->getLocation(),
+ diag::warn_redeclaration_without_import_attribute)
+ << NewDecl;
+ S.Diag(OldDecl->getLocation(), diag::note_previous_declaration);
+ NewDecl->dropAttr<DLLImportAttr>();
+ NewDecl->addAttr(DLLExportAttr::CreateImplicit(
+ S.Context, NewImportAttr->getRange()));
+ }
+ } else if (IsMicrosoftABI && IsSpecialization) {
+ assert(!IsDefinition);
+ // MSVC allows this. Keep the inherited attribute.
} else {
S.Diag(NewDecl->getLocation(),
diag::warn_redeclaration_without_attribute_prev_attribute_ignored)
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3469,6 +3469,8 @@
InGroup<DiagGroup<"dll-attribute-on-redeclaration">>;
def err_attribute_dllimport_function_definition : Error<
"dllimport cannot be applied to non-inline function definition">;
+def err_attribute_dllimport_function_specialization_definition : Error<
+ "cannot define non-inline dllimport template specialization">;
def err_attribute_dll_deleted : Error<
"attribute %q0 cannot be applied to a deleted function">;
def err_attribute_dllimport_data_definition : Error<
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -86,6 +86,36 @@
typedef char int8_a16 __attribute__((aligned(16)));
int8_a16 array[4]; // Now diagnosed as the element size not being a multiple of the array alignment.
+- When compiling for Windows in MSVC compatibility mode (for example by using
+ clang-cl), the compiler will now propagate dllimport/export declspecs in
+ explicit specializations of class template member functions (`Issue 54717
+ <https://github.com/llvm/llvm-project/issues/54717>`_):
+
+ .. code-block:: c++
+
+ template <typename> struct __declspec(dllexport) S {
+ void f();
+ };
+ template<> void S<int>::f() {} // clang-cl will now dllexport this.
+
+ This matches what MSVC does, so it improves compatibility, but it can also
+ cause errors for code which clang-cl would previously accept, for example:
+
+ .. code-block:: c++
+
+ template <typename> struct __declspec(dllexport) S {
+ void f();
+ };
+ template<> void S<int>::f() = delete; // Error: cannot delete dllexport function.
+
+ .. code-block:: c++
+
+ template <typename> struct __declspec(dllimport) S {
+ void f();
+ };
+ template<> void S<int>::f() {}; // Error: cannot define dllimport function.
+
+ These errors also match MSVC's behavior.
What's New in Clang |release|?
==============================
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits