diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 2b2b7391f88fd..1f2bb1edd3b7b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6602,6 +6602,13 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { if (!VD && !MD) continue; + if ((TSK == TSK_ExplicitInstantiationDeclaration || + TSK == TSK_ExplicitInstantiationDefinition) && + Member->hasAttr()) { + // Skip members excluded from instantiation. + continue; + } + if (MD) { // Don't process deleted methods. if (MD->isDeleted()) diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp new file mode 100644 index 0000000000000..b3c804839be1e --- /dev/null +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-windows -fms-extensions -emit-llvm -O0 -o - %s | FileCheck %s + +// Test that dllimport and exclude_from_explicit_instantiation work properly +// together. Specifically, we check that when exclude_from_explicit_instantiation +// is used on a method, the compiler doesn't expect it to be provided externally +// even if it is marked with dllimport. +// +// https://github.com/llvm/llvm-project/issues/40363 + +#define DLLIMPORT __declspec(dllimport) +#define DLLEXPORT __declspec(dllexport) +#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) + +template +struct DLLIMPORT Foo { + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void x() {} +}; + +template +struct Bar { + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void x() {} +}; + +extern template struct Foo; +extern template struct DLLIMPORT Bar; + + +template +struct Baz { + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void f() {} +}; + +template struct DLLEXPORT Baz; + + +void test(Foo& foo, Bar& bar, Baz& baz) { + // Not imported. + // CHECK-DAG: define linkonce_odr dso_local void @"?x@?$Foo@H@@QEAAXXZ" + foo.x(); + + // Not imported. + // CHECK-DAG: define linkonce_odr dso_local void @"?x@?$Bar@H@@QEAAXXZ" + bar.x(); + + // Not exported. + // CHECK-DAG: define linkonce_odr dso_local void @"?f@?$Baz@H@@QEAAXXZ" + baz.f(); +} diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp new file mode 100644 index 0000000000000..bdca2d8895f51 --- /dev/null +++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.dllimport.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-windows -fms-extensions -verify %s + +// Test that an entity marked as both dllimport and exclude_from_explicit_instantiation +// isn't instantiated. + +#define DLLIMPORT __declspec(dllimport) +#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) + +template +struct DLLIMPORT Foo { + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void x(); +}; + +template +struct Bar { + DLLIMPORT EXCLUDE_FROM_EXPLICIT_INSTANTIATION inline void x(); +}; + +template +void Foo::x() { using Fail = typename T::fail; } + +template +DLLIMPORT inline void Bar::x() { using Fail = typename T::fail; } + +// expected-no-diagnostics +template struct Foo; +template struct Bar;