From e5fdb78baffc29df6c772dbe80c113bc22bd72c6 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 25 Jun 2014 18:25:57 +0000 Subject: MS ABI: Propagate class-level DLL attributes to class template specialization bases (PR11170) Consider the following code: template class Base {}; class __declspec(dllexport) class Derived : public Base {} When the base of an exported or imported class is a class template specialization, MSVC will propagate the dll attribute to the base. In the example code, Base becomes a dllexported class. This commit makes Clang do the proopagation when the base hasn't been instantiated yet, and warns about it being unsupported otherwise. This is different from MSVC, which allows changing a specialization back and forth between dllimport and dllexport and seems to let the last one win. Changing the dll attribute after instantiation would be hard for us, and doesn't seem to come up in practice, so I think this is a reasonable limitation to have. MinGW doesn't do this kind of propagation. Differential Revision: http://reviews.llvm.org/D4264 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@211725 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDeclCXX.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5ca53b0a08..35fd22c060 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1296,6 +1296,57 @@ static bool findCircularInheritance(const CXXRecordDecl *Class, return false; } +/// \brief Perform propagation of DLL attributes from a derived class to a +/// templated base class for MS compatibility. +static void propagateDLLAttrToBaseClassTemplate( + Sema &S, CXXRecordDecl *Class, Attr *ClassAttr, + ClassTemplateSpecializationDecl *BaseTemplateSpec, SourceLocation BaseLoc) { + if (getDLLAttr( + BaseTemplateSpec->getSpecializedTemplate()->getTemplatedDecl())) { + // If the base class template has a DLL attribute, don't try to change it. + return; + } + + if (BaseTemplateSpec->getSpecializationKind() == TSK_Undeclared) { + // If the base class is not already specialized, we can do the propagation. + auto *NewAttr = cast(ClassAttr->clone(S.getASTContext())); + NewAttr->setInherited(true); + BaseTemplateSpec->addAttr(NewAttr); + return; + } + + bool DifferentAttribute = false; + if (Attr *SpecializationAttr = getDLLAttr(BaseTemplateSpec)) { + if (!SpecializationAttr->isInherited()) { + // The template has previously been specialized or instantiated with an + // explicit attribute. We should not try to change it. + return; + } + if (SpecializationAttr->getKind() == ClassAttr->getKind()) { + // The specialization already has the right attribute. + return; + } + DifferentAttribute = true; + } + + // The template was previously instantiated or explicitly specialized without + // a dll attribute, or the template was previously instantiated with a + // different inherited attribute. It's too late for us to change the + // attribute, so warn that this is unsupported. + S.Diag(BaseLoc, diag::warn_attribute_dll_instantiated_base_class) + << BaseTemplateSpec->isExplicitSpecialization() << DifferentAttribute; + S.Diag(ClassAttr->getLocation(), diag::note_attribute); + if (BaseTemplateSpec->isExplicitSpecialization()) { + S.Diag(BaseTemplateSpec->getLocation(), + diag::note_template_class_explicit_specialization_was_here) + << BaseTemplateSpec; + } else { + S.Diag(BaseTemplateSpec->getPointOfInstantiation(), + diag::note_template_class_instantiation_was_here) + << BaseTemplateSpec; + } +} + /// \brief Check the validity of a C++ base class specifier. /// /// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics @@ -1362,6 +1413,17 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return nullptr; } + // For the MS ABI, propagate DLL attributes to base class templates. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { + if (Attr *ClassAttr = getDLLAttr(Class)) { + if (auto *BaseTemplate = dyn_cast_or_null( + BaseType->getAsCXXRecordDecl())) { + propagateDLLAttrToBaseClassTemplate(*this, Class, ClassAttr, + BaseTemplate, BaseLoc); + } + } + } + // C++ [class.derived]p2: // The class-name in a base-specifier shall not be an incompletely // defined class. @@ -4361,9 +4423,6 @@ static void checkDLLAttribute(Sema &S, CXXRecordDecl *Class) { // FIXME: MSVC's docs say all bases must be exportable, but this doesn't // seem to be true in practice? - // FIXME: We also need to propagate the attribute upwards to class template - // specialization bases. - for (Decl *Member : Class->decls()) { VarDecl *VD = dyn_cast(Member); CXXMethodDecl *MD = dyn_cast(Member); @@ -4385,7 +4444,7 @@ static void checkDLLAttribute(Sema &S, CXXRecordDecl *Class) { if (InheritableAttr *MemberAttr = getDLLAttr(Member)) { if (S.Context.getTargetInfo().getCXXABI().isMicrosoft() && - !MemberAttr->isInherited()) { + !MemberAttr->isInherited() && !ClassAttr->isInherited()) { S.Diag(MemberAttr->getLocation(), diag::err_attribute_dll_member_of_dll_class) << MemberAttr << ClassAttr; -- cgit v1.2.3