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

Reply via email to