Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6551,6 +6551,8 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
return;
}

TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind();

if (Context.getTargetInfo().shouldDLLImportComdatSymbols() &&
!ClassAttr->isInherited()) {
// Diagnose dll attributes on members of class with dll attribute.
Expand All @@ -6561,6 +6563,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
if (!MemberAttr || MemberAttr->isInherited() || Member->isInvalidDecl())
continue;

if ((TSK == TSK_ExplicitInstantiationDeclaration ||
TSK == TSK_ExplicitInstantiationDefinition) &&
Member->hasAttr<ExcludeFromExplicitInstantiationAttr>())
continue;

Diag(MemberAttr->getLocation(),
diag::err_attribute_dll_member_of_dll_class)
<< MemberAttr << ClassAttr;
Expand All @@ -6583,8 +6590,6 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
!ClassExported &&
cast<DLLImportAttr>(ClassAttr)->wasPropagatedToBaseTemplate();

TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind();

// Ignore explicit dllexport on explicit class template instantiation
// declarations, except in MinGW mode.
if (ClassExported && !ClassAttr->isInherited() &&
Expand All @@ -6601,6 +6606,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
// seem to be true in practice?

for (Decl *Member : Class->decls()) {
if ((TSK == TSK_ExplicitInstantiationDeclaration ||
TSK == TSK_ExplicitInstantiationDefinition) &&
Member->hasAttr<ExcludeFromExplicitInstantiationAttr>())
continue;

VarDecl *VD = dyn_cast<VarDecl>(Member);
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_

// Test that __declspec(dllexport) doesn't instantiate entities marked with
// the exclude_from_explicit_instantiation attribute unless marked as dllexport explicitly.

#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))

template <class T>
struct C {
// This will be instantiated explicitly as an exported function because it
// inherits dllexport from the class instantiation.
void to_be_exported() noexcept;

// This will be instantiated implicitly as an exported function because it is
// marked as dllexport explicitly.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void to_be_exported_explicitly() noexcept;

// This will be instantiated implicitly but won't be exported.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported() noexcept;

// This won't be instantiated.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
};

template <class T> void C<T>::to_be_exported() noexcept {}
template <class T> void C<T>::to_be_exported_explicitly() noexcept {}
template <class T> void C<T>::not_to_be_exported() noexcept {}
template <class T> void C<T>::not_to_be_instantiated() noexcept {}

// MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any
// MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any
// MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any
// GNU: $_ZN1CIiE14to_be_exportedEv = comdat any
// GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any
// GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any

// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C@H@@QEAAXXZ"
// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE14to_be_exportedEv
template struct __declspec(dllexport) C<int>;

void use() {
C<int> c;

// MSC: call void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE25to_be_exported_explicitlyEv
c.to_be_exported_explicitly(); // implicitly instantiated here

// MSC: call void @"?not_to_be_exported@?$C@H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE18not_to_be_exportedEv
c.not_to_be_exported(); // implicitly instantiated here
};

// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ"
// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE25to_be_exported_explicitlyEv

// MSC: define linkonce_odr dso_local void @"?not_to_be_exported@?$C@H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_exportedEv
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_
// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_
// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_

// Test that __declspec(dllimport) doesn't instantiate entities marked with
// the exclude_from_explicit_instantiation attribute unless marked as dllimport explicitly.

#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))

template <class T>
struct C {
// This will be instantiated explicitly as an imported function because it
// inherits dllimport from the class instantiation.
void to_be_imported() noexcept;

// This will be instantiated implicitly as an imported function because it is
// marked as dllimport explicitly.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void to_be_imported_explicitly() noexcept;

// This will be instantiated implicitly but won't be imported.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept;

// This won't be instantiated.
EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept;
};

template <class T> void C<T>::to_be_imported() noexcept {}
template <class T> void C<T>::not_to_be_imported() noexcept {}
template <class T> void C<T>::not_to_be_instantiated() noexcept {}

// MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any
// GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any
extern template struct __declspec(dllimport) C<int>;

void use() {
C<int> c;

// MSC: call void @"?to_be_imported@?$C@H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE14to_be_importedEv
c.to_be_imported();

// MSC: call void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE25to_be_imported_explicitlyEv
c.to_be_imported_explicitly(); // implicitly instantiated here

// MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
// GNU: call void @_ZN1CIiE18not_to_be_importedEv
c.not_to_be_imported(); // implicitly instantiated here
};

// MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ"
// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv

// MSC: declare dllimport void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ"
// GNU: declare dllimport void @_ZN1CIiE25to_be_imported_explicitlyEv

// MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C@H@@QEAAXXZ"
// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv
Loading