From f0ee4dc5886ba64f430fb4c5c85291f300705326 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Thu, 15 Jan 2015 21:18:30 +0000 Subject: [PATCH] Warn about dllexported explicit class template instantiation declarations (PR22035) Clang would previously become confused and crash here. It does not make a lot of sense to export these, so warning seems appropriate. MSVC will export some member functions for this kind of specializations, whereas MinGW ignores the dllexport-edness. The latter behaviour seems better. Differential Revision: http://reviews.llvm.org/D6984 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@226208 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ lib/Sema/SemaDeclCXX.cpp | 11 +++++++--- lib/Sema/SemaTemplate.cpp | 24 +++++++++++++++++++--- test/CodeGenCXX/dllexport.cpp | 11 ++++++++++ test/SemaCXX/dllexport.cpp | 9 ++++++-- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9e3393e3e1..d47d5e1eca 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2201,6 +2201,9 @@ def err_attribute_dllimport_static_field_definition : Error< 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>; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 54de4b9793..79529d5547 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4709,15 +4709,20 @@ static void checkDLLAttribute(Sema &S, CXXRecordDecl *Class) { 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); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 67c36a5fb5..254e78fa01 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -7165,9 +7165,27 @@ Sema::ActOnExplicitInstantiation(Scope *S, // 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); diff --git a/test/CodeGenCXX/dllexport.cpp b/test/CodeGenCXX/dllexport.cpp index 93bd1f5fe9..53c7de2ee2 100644 --- a/test/CodeGenCXX/dllexport.cpp +++ b/test/CodeGenCXX/dllexport.cpp @@ -631,6 +631,17 @@ template struct ExplicitInstConstexprMembers { }; 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 //===----------------------------------------------------------------------===// diff --git a/test/SemaCXX/dllexport.cpp b/test/SemaCXX/dllexport.cpp index 5d002ac81e..3bb9d224f7 100644 --- a/test/SemaCXX/dllexport.cpp +++ b/test/SemaCXX/dllexport.cpp @@ -353,10 +353,10 @@ ImplicitlyInstantiatedExportedTemplate implicitlyInstantiatedExp // 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 @@ template struct __declspec(dllexport) ExportedBaseClassTemplateOfEx }; 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 -- 2.40.0