Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2201,6 +2201,9 @@ def warn_attribute_dllimport_static_field_definition : Warning< "definition of dllimport static field">, InGroup>; +def warn_attribute_dllexport_explicit_instantiation_decl : Warning< + "explicit instantiation declaration should not be 'dllexport'">, + InGroup>; def warn_invalid_initializer_from_system_header : Warning< "invalid constructor form class in system header, should not be explicit">, InGroup>; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -4709,15 +4709,20 @@ const bool ClassExported = ClassAttr->getKind() == attr::DLLExport; const bool ClassImported = !ClassExported; + TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind(); + + // Don't dllexport explicit class template instantiation declarations. + if (ClassExported && TSK == TSK_ExplicitInstantiationDeclaration) { + Class->dropAttr(); + return; + } + // Force declaration of implicit members so they can inherit the attribute. S.ForceDeclarationOfImplicitMembers(Class); // FIXME: MSVC's docs say all bases must be exportable, but this doesn't // seem to be true in practice? - TemplateSpecializationKind TSK = - Class->getTemplateSpecializationKind(); - for (Decl *Member : Class->decls()) { VarDecl *VD = dyn_cast(Member); CXXMethodDecl *MD = dyn_cast(Member); Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -7165,9 +7165,27 @@ // There are two forms of explicit instantiation: an explicit instantiation // definition and an explicit instantiation declaration. An explicit // instantiation declaration begins with the extern keyword. [...] - TemplateSpecializationKind TSK - = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition - : TSK_ExplicitInstantiationDeclaration; + TemplateSpecializationKind TSK = ExternLoc.isInvalid() + ? TSK_ExplicitInstantiationDefinition + : TSK_ExplicitInstantiationDeclaration; + + if (TSK == TSK_ExplicitInstantiationDeclaration) { + // Check for dllexport class template instantiation declarations. + for (AttributeList *A = Attr; A; A = A->getNext()) { + if (A->getKind() == AttributeList::AT_DLLExport) { + Diag(ExternLoc, + diag::warn_attribute_dllexport_explicit_instantiation_decl); + Diag(A->getLoc(), diag::note_attribute); + break; + } + } + + if (auto *A = ClassTemplate->getTemplatedDecl()->getAttr()) { + Diag(ExternLoc, + diag::warn_attribute_dllexport_explicit_instantiation_decl); + Diag(A->getLocation(), diag::note_attribute); + } + } // Translate the parser's template argument list in our AST format. TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc); Index: test/CodeGenCXX/dllexport.cpp =================================================================== --- test/CodeGenCXX/dllexport.cpp +++ test/CodeGenCXX/dllexport.cpp @@ -631,6 +631,17 @@ }; template struct __declspec(dllexport) ExplicitInstConstexprMembers; +template struct ExplicitInstantiationDeclTemplate { void f() {} }; +extern template struct __declspec(dllexport) ExplicitInstantiationDeclTemplate; +USEMEMFUNC(ExplicitInstantiationDeclTemplate, f); +// M32-DAG: {{declare|define available_externally}} x86_thiscallcc void @"\01?f@?$ExplicitInstantiationDeclTemplate@H@@QAEXXZ" + +template struct __declspec(dllexport) ExplicitInstantiationDeclExportedTemplate { void f() {} }; +extern template struct ExplicitInstantiationDeclExportedTemplate; +USEMEMFUNC(ExplicitInstantiationDeclExportedTemplate, f); +// M32-DAG: {{declare|define available_externally}} x86_thiscallcc void @"\01?f@?$ExplicitInstantiationDeclExportedTemplate@H@@QAEXXZ" + + //===----------------------------------------------------------------------===// // Classes with template base classes //===----------------------------------------------------------------------===// Index: test/SemaCXX/dllexport.cpp =================================================================== --- test/SemaCXX/dllexport.cpp +++ test/SemaCXX/dllexport.cpp @@ -353,10 +353,10 @@ // Don't instantiate class members of templates with explicit instantiation declarations, even if they are exported. struct IncompleteType2; -template struct __declspec(dllexport) ExportedTemplateWithExplicitInstantiationDecl { +template struct __declspec(dllexport) ExportedTemplateWithExplicitInstantiationDecl { // expected-note{{attribute is here}} int f() { return sizeof(T); } // no-error }; -extern template struct ExportedTemplateWithExplicitInstantiationDecl; +extern template struct ExportedTemplateWithExplicitInstantiationDecl; // expected-warning{{explicit instantiation declaration should not be 'dllexport'}} // Instantiate class members for explicitly instantiated exported templates. struct IncompleteType3; // expected-note{{forward declaration of 'IncompleteType3'}} @@ -386,7 +386,12 @@ }; struct __declspec(dllexport) ExportedBaseClass2 : public ExportedBaseClassTemplateOfExportedClass {}; +// Warn about explicit instantiation declarations of dllexport classes. +template struct ExplicitInstantiationDeclTemplate {}; +extern template struct __declspec(dllexport) ExplicitInstantiationDeclTemplate; // expected-warning{{explicit instantiation declaration should not be 'dllexport'}} expected-note{{attribute is here}} +template struct __declspec(dllexport) ExplicitInstantiationDeclExportedTemplate {}; // expected-note{{attribute is here}} +extern template struct ExplicitInstantiationDeclExportedTemplate; // expected-warning{{explicit instantiation declaration should not be 'dllexport'}} //===----------------------------------------------------------------------===// // Classes with template base classes