summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2014-06-25 18:25:57 +0000
committerHans Wennborg <hans@hanshq.net>2014-06-25 18:25:57 +0000
commite5fdb78baffc29df6c772dbe80c113bc22bd72c6 (patch)
tree58027e501f90d4c522627694a29d701b6e5e0b64 /lib
parentccaf37e1c9c5e4bf74224ef779d62b3ba05e3615 (diff)
downloadclang-e5fdb78baffc29df6c772dbe80c113bc22bd72c6.tar.gz
clang-e5fdb78baffc29df6c772dbe80c113bc22bd72c6.tar.bz2
clang-e5fdb78baffc29df6c772dbe80c113bc22bd72c6.tar.xz
MS ABI: Propagate class-level DLL attributes to class template specialization bases (PR11170)
Consider the following code: template <typename T> class Base {}; class __declspec(dllexport) class Derived : public Base<int> {} 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<int> 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
Diffstat (limited to 'lib')
-rw-r--r--lib/Sema/SemaDeclCXX.cpp67
1 files changed, 63 insertions, 4 deletions
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<InheritableAttr>(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<ClassTemplateSpecializationDecl>(
+ 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<VarDecl>(Member);
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(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;