Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -7491,6 +7491,10 @@ IdentifierInfo *AliasName, SourceLocation AliasLocation, IdentifierInfo *ClassName, SourceLocation ClassLocation); + /// \brief Make an existing DLL attribute on a class take effect. + void ActOnDLLAttr(ClassTemplateSpecializationDecl *Def, + InheritableAttr *Attr); + bool CheckForwardProtocolDeclarationForCircularDependency( IdentifierInfo *PName, SourceLocation &PLoc, SourceLocation PrevLoc, Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -7435,6 +7435,23 @@ return false; } +void Sema::ActOnDLLAttr(ClassTemplateSpecializationDecl *Def, + InheritableAttr *Attr) { + // We reject explicit instantiations in class scope, so there should + // never be any delayed exported classes to worry about. + assert(DelayedDllExportClasses.empty() && + "delayed exports present at explicit instantiation"); + checkClassLevelDLLAttribute(Def); + referenceDLLExportedClassMethods(); + + // Propagate attribute to base class templates. + for (auto &B : Def->bases()) { + if (auto *BT = dyn_cast_or_null( + B.getType()->getAsCXXRecordDecl())) + propagateDLLAttrToBaseClassTemplate(Def, Attr, BT, B.getLocStart()); + } +} + // Explicit instantiation of a class template specialization DeclResult Sema::ActOnExplicitInstantiation(Scope *S, @@ -7681,23 +7698,35 @@ getDLLAttr(Specialization)->clone(getASTContext())); A->setInherited(true); Def->addAttr(A); - - // We reject explicit instantiations in class scope, so there should - // never be any delayed exported classes to worry about. - assert(DelayedDllExportClasses.empty() && - "delayed exports present at explicit instantiation"); - checkClassLevelDLLAttribute(Def); - referenceDLLExportedClassMethods(); - - // Propagate attribute to base class templates. - for (auto &B : Def->bases()) { - if (auto *BT = dyn_cast_or_null( - B.getType()->getAsCXXRecordDecl())) - propagateDLLAttrToBaseClassTemplate(Def, A, BT, B.getLocStart()); - } + ActOnDLLAttr(Def, A); } } + // Fix a TSK_ImplicitInstantiation followed by a + // TSK_ExplicitInstantiationDefinition + if (Old_TSK == TSK_ImplicitInstantiation && + Specialization->hasAttr() && + (Context.getTargetInfo().getCXXABI().isMicrosoft() || + Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment())) { + // In the MS ABI, an explicit instantiation definition can add a dll + // attribute to a template with a previous implicit instantiation. + // or implicit instantiation. MinGW doesn't allow this. We limit clang to + // only adding dllexport, to avoid potentially strange codegen behavior. + // For example, if we extend this conditional to dllimport, and we have a + // source file calling a method on an implicitly instantiated template + // class instance and then declaring a dllimport explicit instantiation + // definition for the same template class, the codegen for the method call + // will not respect the dllimport, while it will with cl. The Def will + // already have the DLL attribute, since the Def and Specialization will + // be the same in the case of Old_TSK == TSK_ImplicitInstantiation, and we + // already added the attribute to the Specialization; we just need to make + // it take effect. + assert(Def == Specialization && + "Def and Specialization should match for implicit instantiation"); + auto *A = cast(Def->getAttr()); + ActOnDLLAttr(Def, A); + } + // Set the template specialization kind. Make sure it is set before // instantiating the members which will trigger ASTConsumer callbacks. Specialization->setTemplateSpecializationKind(TSK); Index: test/CodeGenCXX/dllexport.cpp =================================================================== --- test/CodeGenCXX/dllexport.cpp +++ test/CodeGenCXX/dllexport.cpp @@ -771,6 +771,13 @@ // M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.ExplicitInstantiationDeclExportedDefTemplate* @"\01??0?$ExplicitInstantiationDeclExportedDefTemplate@H@@QAE@XZ" // G32-DAG: define weak_odr x86_thiscallcc void @_ZN44ExplicitInstantiationDeclExportedDefTemplateIiE1fEv +template struct ImplicitInstantiationExplicitInstantiationDefExportedTemplate { void f() {} }; +ImplicitInstantiationExplicitInstantiationDefExportedTemplate ImplicitInstantiationExplicitInstantiationDefExportedTemplateInstance; +template class __declspec(dllexport) ImplicitInstantiationExplicitInstantiationDefExportedTemplate; +USEMEMFUNC(ImplicitInstantiationExplicitInstantiationDefExportedTemplate, f); +// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?f@?$ImplicitInstantiationExplicitInstantiationDefExportedTemplate@H@@QAEXXZ" +// G32-DAG: define weak_odr x86_thiscallcc void @_ZN61ImplicitInstantiationExplicitInstantiationDefExportedTemplateIiE1fEv + namespace { struct InternalLinkageType {}; } struct __declspec(dllexport) PR23308 { void f(InternalLinkageType*); Index: test/CodeGenCXX/windows-itanium-dllexport.cpp =================================================================== --- test/CodeGenCXX/windows-itanium-dllexport.cpp +++ test/CodeGenCXX/windows-itanium-dllexport.cpp @@ -23,3 +23,8 @@ // CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcEaSERKS0_ // CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv +c g; +template class __declspec(dllexport) c; + +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIdEaSERKS0_ +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv