Author: Tomohiro Kashiwada Date: 2026-03-04T14:10:23+01:00 New Revision: 0af2d43e06415ce1e8a5d49e864c3881048dd08b
URL: https://github.com/llvm/llvm-project/commit/0af2d43e06415ce1e8a5d49e864c3881048dd08b DIFF: https://github.com/llvm/llvm-project/commit/0af2d43e06415ce1e8a5d49e864c3881048dd08b.diff LOG: [Clang] Warn if both of `dllexport`/`dllimport` and `exclude_from_explicit_instantiation` are specified (#183515) The attributes `exclude_from_explicit_instantiation` and `dllexport`/`dllimport` serve opposite purposes. Therefore, if an entity has both attributes, drop one with a warning, depending on the context of the declaration. In a template context, the `exclude_from_explicit_instantiation` attribute takes precedence over the `dllexport` or `dllimport` attribute. Conversely, the `dllexport` and `dllimport` attributes are prioritized, in a non-template context. Added: clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp Modified: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaDeclAttr.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a2d12a3a2c758..8882ac9b8c0a8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3790,6 +3790,15 @@ def warn_dllimport_dropped_from_inline_function : Warning< def warn_nothrow_attribute_ignored : Warning<"'nothrow' attribute conflicts with" " exception specification; attribute ignored">, InGroup<IgnoredAttributes>; +def warn_dllattr_ignored_exclusion_takes_precedence : Warning< + "%0 attribute ignored; %1 takes precedence">, + InGroup<IgnoredAttributes>; +def warn_attribute_ignored_on_non_member : + Warning<"%0 attribute ignored on a non-member declaration">, + InGroup<IgnoredAttributes>; +def warn_attribute_ignored_in_non_template : + Warning<"%0 attribute ignored in a non-template context">, + InGroup<IgnoredAttributes>; def warn_attribute_ignored_on_non_definition : Warning<"%0 attribute ignored on a non-definition declaration">, InGroup<IgnoredAttributes>; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 3abc69d0e4b96..1b7f41061061d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -48,6 +48,7 @@ #include "clang/Sema/SemaBPF.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaHLSL.h" +#include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaM68k.h" #include "clang/Sema/SemaMIPS.h" #include "clang/Sema/SemaMSP430.h" @@ -705,6 +706,24 @@ static void handleExcludeFromExplicitInstantiationAttr(Sema &S, Decl *D, << AL << /*IsMember=*/!isa<CXXRecordDecl>(D); return; } + + if (auto *DA = getDLLAttr(D); DA && !DA->isInherited()) { + if (auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { + if (RD->isTemplated()) { + S.Diag(DA->getLoc(), + diag::warn_dllattr_ignored_exclusion_takes_precedence) + << DA << AL; + D->dropAttrs<DLLExportAttr, DLLImportAttr>(); + } else { + S.Diag(AL.getLoc(), diag::warn_attribute_ignored_in_non_template) << AL; + return; + } + } else { + S.Diag(AL.getLoc(), diag::warn_attribute_ignored_on_non_member) << AL; + return; + } + } + D->addAttr(::new (S.Context) ExcludeFromExplicitInstantiationAttr(S.Context, AL)); } @@ -6469,6 +6488,22 @@ static void handleDLLAttr(Sema &S, Decl *D, const ParsedAttr &A) { } } + if (auto *EA = D->getAttr<ExcludeFromExplicitInstantiationAttr>()) { + if (auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { + if (RD->isTemplated()) { + S.Diag(A.getRange().getBegin(), + diag::warn_dllattr_ignored_exclusion_takes_precedence) + << A << EA; + return; + } + S.Diag(EA->getLoc(), diag::warn_attribute_ignored_in_non_template) << EA; + D->dropAttr<ExcludeFromExplicitInstantiationAttr>(); + } else { + S.Diag(EA->getLoc(), diag::warn_attribute_ignored_on_non_member) << EA; + D->dropAttr<ExcludeFromExplicitInstantiationAttr>(); + } + } + Attr *NewAttr = A.getKind() == ParsedAttr::AT_DLLExport ? (Attr *)S.mergeDLLExportAttr(D, A) : (Attr *)S.mergeDLLImportAttr(D, A); 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..5943f99e14cad --- /dev/null +++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.ignore-dllattr.cpp @@ -0,0 +1,172 @@ +// 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 attaching the exclude_from_explicit_instantiation attribute and +// either the dllexport or dllimport attribute together causes a warning. +// One of them is ignored, depending on the context that is declared. + +#define EXCLUDE_ATTR __attribute__((exclude_from_explicit_instantiation)) + +// Test that exclude_from_explicit_instantiation takes precedence over +// dllexport/dllimport in a template context. +template <class T> +struct class_tmpl_no_instantiated { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) void fn_excluded_imported(); // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + __declspec(dllexport) EXCLUDE_ATTR void fn_exported_excluded(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + __declspec(dllimport) EXCLUDE_ATTR void fn_imported_excluded(); // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + + EXCLUDE_ATTR __declspec(dllexport) static int var_excluded_exported; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + EXCLUDE_ATTR __declspec(dllimport) static int var_excluded_imported; // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + __declspec(dllexport) EXCLUDE_ATTR static int var_exported_excluded; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + __declspec(dllimport) EXCLUDE_ATTR static int var_imported_excluded; // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + + struct EXCLUDE_ATTR __declspec(dllexport) nested_excluded_exported {}; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + struct EXCLUDE_ATTR __declspec(dllimport) nested_excluded_imported {}; // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + struct __declspec(dllexport) EXCLUDE_ATTR nested_exported_excluded {}; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + struct __declspec(dllimport) EXCLUDE_ATTR nested_imported_excluded {}; // expected-warning{{'dllimport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + + template <class U> + struct EXCLUDE_ATTR __declspec(dllexport) nested_tmpl_excluded_exported {}; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + template <class U> + EXCLUDE_ATTR __declspec(dllexport) static T var_template_excluded; // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + template <class U> + EXCLUDE_ATTR __declspec(dllexport) void fn_template_excluded(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + + struct nested { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + }; + + struct EXCLUDE_ATTR nested_excluded { + __declspec(dllexport) void fn_exported(); + }; + struct __declspec(dllexport) nested_exported { + EXCLUDE_ATTR void fn_excluded(); + }; + struct __declspec(dllimport) nested_imported { + EXCLUDE_ATTR void fn_excluded(); + }; + + template <class U> + struct nested_tmpl { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + }; +}; + +// Test that a class-level dll attribute doesn't cause a warning on an excluded member. +template <class T> +struct __declspec(dllexport) class_tmpl_exported { + EXCLUDE_ATTR void fn_excluded(); +}; +template struct class_tmpl_exported<int>; +void use_class_tmpl_exported() { class_tmpl_exported<long>().fn_excluded(); } + +template <class T> +struct __declspec(dllimport) class_tmpl_imported { + EXCLUDE_ATTR void fn_excluded(); +}; +template struct class_tmpl_imported<int>; +void use_class_tmpl_imported() { class_tmpl_imported<long>().fn_excluded(); } + +// Test that a dll attribute on an explicit instantiation doesn't cause a warning on +// an excluded member. +template <class T> +struct class_tmpl_explicit_inst { + EXCLUDE_ATTR void fn_excluded(); + EXCLUDE_ATTR static T var_excluded; + struct EXCLUDE_ATTR nested_excluded { + __declspec(dllexport) void fn_exported(); + __declspec(dllimport) void fn_imported(); + }; + + struct __declspec(dllexport) nested_exported { + EXCLUDE_ATTR void fn_excluded(); + }; + struct __declspec(dllimport) nested_imported { + EXCLUDE_ATTR void fn_excluded(); + }; +}; +extern template struct __declspec(dllimport) class_tmpl_explicit_inst<long>; +template struct __declspec(dllexport) class_tmpl_explicit_inst<int>; + +// Test that exclude_from_explicit_instantiation is ignored in a non-template context. +struct class_nontmpl { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + EXCLUDE_ATTR __declspec(dllimport) void fn_excluded_imported(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + __declspec(dllexport) EXCLUDE_ATTR void fn_exported_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + __declspec(dllimport) EXCLUDE_ATTR void fn_imported_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + + EXCLUDE_ATTR __declspec(dllexport) static int var_excluded_exported; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + EXCLUDE_ATTR __declspec(dllimport) static int var_excluded_imported; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + __declspec(dllexport) EXCLUDE_ATTR static int var_exported_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + __declspec(dllimport) EXCLUDE_ATTR static int var_imported_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + + struct EXCLUDE_ATTR __declspec(dllexport) nested_excluded_exported {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + struct EXCLUDE_ATTR __declspec(dllimport) nested_excluded_imported {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + struct __declspec(dllexport) EXCLUDE_ATTR nested_exported_excluded {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + struct __declspec(dllimport) EXCLUDE_ATTR nested_imported_excluded {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + + template <class T> + struct EXCLUDE_ATTR __declspec(dllexport) class_template_excluded {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + template <class T> + EXCLUDE_ATTR __declspec(dllexport) static T var_template_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + template <class T> + EXCLUDE_ATTR __declspec(dllexport) void fn_template_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + + struct nested { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored in a non-template context}} + }; + + struct EXCLUDE_ATTR nested_excluded { + __declspec(dllexport) void fn_excluded_exported(); + }; + struct __declspec(dllexport) nested_exported { + EXCLUDE_ATTR void fn_excluded(); + }; + struct __declspec(dllimport) nested_imported { + EXCLUDE_ATTR void fn_excluded(); + }; + + template <class T> + struct nested_tmpl { + EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'dllexport' attribute ignored; 'exclude_from_explicit_instantiation' takes precedence}} + }; +}; + +// Test that exclude_from_explicit_instantiation is ignored on a non-member entity. +EXCLUDE_ATTR __declspec(dllexport) void fn_excluded_exported(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +EXCLUDE_ATTR __declspec(dllimport) void fn_excluded_imported(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +__declspec(dllexport) EXCLUDE_ATTR void fn_exported_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +__declspec(dllimport) EXCLUDE_ATTR void fn_imported_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} + +EXCLUDE_ATTR __declspec(dllexport) int var_excluded_exported; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +EXCLUDE_ATTR __declspec(dllimport) int var_excluded_imported; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +__declspec(dllexport) EXCLUDE_ATTR int var_exported_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +__declspec(dllimport) EXCLUDE_ATTR int var_imported_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} + +struct EXCLUDE_ATTR __declspec(dllexport) class_excluded_exported {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +struct EXCLUDE_ATTR __declspec(dllimport) class_excluded_imported {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +struct __declspec(dllexport) EXCLUDE_ATTR class_exported_excluded {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +struct __declspec(dllimport) EXCLUDE_ATTR class_imported_excluded {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} + +template <class T> +struct EXCLUDE_ATTR __declspec(dllexport) class_tmpl_excluded_exported {}; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +template <class T> +EXCLUDE_ATTR __declspec(dllexport) T var_template_excluded; // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} +template <class T> +EXCLUDE_ATTR __declspec(dllexport) void fn_template_excluded(); // expected-warning{{'exclude_from_explicit_instantiation' attribute ignored on a non-member declaration}} + +EXCLUDE_ATTR void fn_excluded(); + +EXCLUDE_ATTR int var_excluded; + +struct EXCLUDE_ATTR class_excluded { + __declspec(dllexport) void fn_excluded_exported(); +}; +struct __declspec(dllexport) class_exported { + EXCLUDE_ATTR void fn_excluded(); +}; +struct __declspec(dllimport) class_imported { + EXCLUDE_ATTR void fn_excluded(); +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
