llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Tomohiro Kashiwada (kikairoya) <details> <summary>Changes</summary> Drop `dllexport` and `dllimport` attributes from a entity that is declared with the `exclude_from_explicit_instantiation` attribute. Despite its name, the exclude attribute takes precedence over the DLL attributes, even if the entity is implicitly instantiated. This makes the semantics of the attribute simpler -- "excluded members are never DLL-exported/imported". In a non-template context, the exclude attribute is already dropped, so the DLL attributes take precedence. --- Full diff: https://github.com/llvm/llvm-project/pull/183515.diff 3 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+7) - (added) clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp (+98) ``````````diff diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8a3b9de19ad32..3bded7c6f78e7 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3784,6 +3784,9 @@ def warn_redeclaration_without_attribute_prev_attribute_ignored : Warning< def warn_redeclaration_without_import_attribute : Warning< "%q0 redeclared without 'dllimport' attribute: 'dllexport' attribute added">, InGroup<MicrosoftInconsistentDllImport>; +def warn_dllattr_ignored_exclusion_takes_precedence : Warning< + "%0 attribute ignored; %1 takes precedence">, + InGroup<IgnoredAttributes>; def warn_dllimport_dropped_from_inline_function : Warning< "%q0 redeclared inline; %1 attribute ignored">, InGroup<IgnoredAttributes>; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 5837ecd6b9163..783ee803f9d26 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7229,6 +7229,13 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; for (auto *D : Record->decls()) { + if (auto *EA = D->getAttr<ExcludeFromExplicitInstantiationAttr>()) { + if (auto *DA = getDLLAttr(D)) { + Diag(DA->getRange().getBegin(), diag::warn_dllattr_ignored_exclusion_takes_precedence) << *DA << EA; + D->dropAttrs<DLLExportAttr, DLLImportAttr>(); + } + } + if (auto *M = dyn_cast<CXXMethodDecl>(D)) { // FIXME: We could do this check for dependent types with non-dependent // bases. diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp new file mode 100644 index 0000000000000..af15ed9f34688 --- /dev/null +++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-mingw -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-cygwin -fsyntax-only -verify %s + +// Test that memberwise dllexport and dllimport are warned if the +// exclude_from_explicit_instantiation attribute is attached. + +#define EXCLUDE_ATTR __attribute__((exclude_from_explicit_instantiation)) + +template <class T> +struct C { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); +// expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) void fn_excluded_imported(); +// expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllexport) static int var_excluded_exported; +// expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) static int var_excluded_imported; +// expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + struct EXCLUDE_ATTR __declspec(dllexport) nested_excluded_exported {}; +// expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + struct EXCLUDE_ATTR __declspec(dllimport) nested_excluded_imported {}; +// expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + + // No warnings here since nested_excluded is not instantiated. + struct EXCLUDE_ATTR nested_excluded { + __declspec(dllexport) void fn_exported(); + __declspec(dllimport) void fn_imported(); + }; + // This too. nested_exported is not instantiated. + struct __declspec(dllexport) nested_exported { + EXCLUDE_ATTR void fn_excluded(); + EXCLUDE_ATTR static int var_excluded; + }; + // The same. nested_imported is not instantiated. + struct __declspec(dllimport) nested_imported { + EXCLUDE_ATTR void fn_excluded(); + EXCLUDE_ATTR static int var_excluded; + }; + + struct nested { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); + // expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) void fn_excluded_imported(); + // expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllexport) static int var_excluded_exported; + // expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) static int var_excluded_imported; + // expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + }; +}; + +// Test that class-level dll attributes doesn't cause a warning on an excluded member. +template <class T> +struct __declspec(dllexport) DE { + EXCLUDE_ATTR void fn_excluded(); +}; +template struct DE<int>; + +template <class T> +struct __declspec(dllimport) DI { + EXCLUDE_ATTR void fn_excluded(); +}; +template struct DI<int>; + +// Test that dll attributes on explicit instantiation doesn't cause a warning on +// an excluded member. +// However, a non-template nested type may be warned on an excluded member by +// its dll attribute. +template <class T> +struct E { + EXCLUDE_ATTR void fn_excluded(); + struct EXCLUDE_ATTR nested_excluded { + __declspec(dllexport) void fn_exported(); + __declspec(dllimport) void fn_imported(); + }; + + struct __declspec(dllexport) nested_exported_1 { + // expected-warning@-1{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR void fn_excluded(); + }; + struct __declspec(dllimport) nested_imported_1 { + // expected-warning@-1{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR void fn_excluded(); + }; + + // Make sure that any warning isn't emitted if the nested type has no excluded members. + struct __declspec(dllexport) nested_exported_2 { + void fn(); + }; + struct __declspec(dllimport) nested_imported_2 { + void fn(); + }; +}; +extern template struct __declspec(dllimport) E<long>; +template struct __declspec(dllexport) E<int>; +// expected-note@-1{{in instantiation of member class 'E<int>::nested_exported_1' requested here}} +// expected-note@-2{{in instantiation of member class 'E<int>::nested_imported_1' requested here}} `````````` </details> https://github.com/llvm/llvm-project/pull/183515 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
