[clang] [clang] Diagnose when spelled 'abi_tag' on namespace doesn't match applied one. (PR #221039)
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 10:17:31 PDT 2026
https://github.com/vsapsai updated https://github.com/llvm/llvm-project/pull/221039
>From ca0d3577a12d3ff3a23377add7d02ad03add1fa9 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <vsapsai at apple.com>
Date: Wed, 2 Sep 2026 23:52:33 -0700
Subject: [PATCH 1/2] [clang] Diagnose when spelled 'abi_tag' on namespace
doesn't match applied one.
'abi_tag' on a namespace means applying it to decls within this
namespace. You can reopen a namespace with the same name later. And in
this case clang uses 'abi_tag' from the canonical namespace decl, not
the attribute specified for this reopened namespace. This difference
between the spelled and applied 'abi_tag' can be unexpected and confusing.
Don't change the existing behavior but add a diagnostic notifying about
such mismatches.
---
clang/docs/ReleaseNotes.md | 5 ++
clang/include/clang/Basic/DiagnosticGroups.td | 2 +
.../clang/Basic/DiagnosticSemaKinds.td | 6 ++
clang/lib/Sema/SemaDecl.cpp | 61 ++++++++++++++-----
clang/lib/Sema/SemaDeclCXX.cpp | 3 +-
clang/test/SemaCXX/attr-abi-tag-syntax.cpp | 26 +++++++-
6 files changed, 85 insertions(+), 18 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a1f24a8caedae..a5f8fb5a971f3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -518,6 +518,11 @@ features cannot lower the translation-unit ABI level;
- Improve Clang diagnoses when unary `__imag` operator with non-complex type operand is used as lvalue. (GH222383)
+- Added `-Wabi-tag` to diagnose when the `abi_tag` attribute spelled on a
+ namespace declaration doesn't match the one actually applied, which is taken
+ from the first declaration of that namespace. The diagnostic is an error by
+ default and can be downgraded with `-Wno-error=abi-tag`.
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 1da7698944b24..9435578145ddf 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1868,6 +1868,8 @@ def UnknownArgument : DiagGroup<"unknown-argument">;
def UnsupportedABI : DiagGroup<"unsupported-abi">;
+def IgnoredAbiTagAttr : DiagGroup<"abi-tag">;
+
// A warning group for warnings about code that clang accepts when
// compiling OpenCL C/C++ but which is not compatible with the SPIR(-V) spec.
def SpirCompat : DiagGroup<"spir-compat">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fca68f292f667..82911fa7a9601 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6492,6 +6492,12 @@ def err_redefinition_extern_inline : Error<
def warn_attr_abi_tag_namespace : Warning<
"'abi_tag' attribute on %select{non-inline|anonymous}0 namespace ignored">,
InGroup<IgnoredAttributes>;
+def warn_abi_tag_ignored_missing : Warning<
+ "absent 'abi_tag' attribute is ignored, applying 'abi_tag' %0">,
+ InGroup<IgnoredAbiTagAttr>, DefaultError;
+def warn_abi_tag_ignored_different : Warning<
+ "'abi_tag' %0 is ignored, applying %select{no 'abi_tag'|'abi_tag' %2}1">,
+ InGroup<IgnoredAbiTagAttr>, DefaultError;
def err_abi_tag_on_redeclaration : Error<
"cannot add 'abi_tag' attribute in a redeclaration">;
def err_new_abi_tag_on_redeclaration : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e52f99b4cd98d..9f6949d853f09 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2961,11 +2961,13 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(),
AA->getMessage(), AA->getStrict(), AA->getReplacement(), AMK,
AA->getPriority(), AA->getEnvironment(), InferredPlatformII);
- } else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr))
- NewAttr = S.mergeVisibilityAttr(D, *VA, VA->getVisibility());
- else if (const auto *VA = dyn_cast<TypeVisibilityAttr>(Attr))
- NewAttr = S.mergeTypeVisibilityAttr(D, *VA, VA->getVisibility());
- else if (const auto *ImportA = dyn_cast<DLLImportAttr>(Attr))
+ } else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr)) {
+ if (!isa<NamespaceDecl>(D))
+ NewAttr = S.mergeVisibilityAttr(D, *VA, VA->getVisibility());
+ } else if (const auto *VA = dyn_cast<TypeVisibilityAttr>(Attr)) {
+ if (!isa<NamespaceDecl>(D))
+ NewAttr = S.mergeTypeVisibilityAttr(D, *VA, VA->getVisibility());
+ } else if (const auto *ImportA = dyn_cast<DLLImportAttr>(Attr))
NewAttr = S.mergeDLLImportAttr(D, *ImportA);
else if (const auto *ExportA = dyn_cast<DLLExportAttr>(Attr))
NewAttr = S.mergeDLLExportAttr(D, *ExportA);
@@ -3368,20 +3370,47 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
}
}
- // Re-declaration cannot add abi_tag's.
- if (const auto *NewAbiTagAttr = New->getAttr<AbiTagAttr>()) {
- if (const auto *OldAbiTagAttr = Old->getAttr<AbiTagAttr>()) {
- for (const auto &NewTag : NewAbiTagAttr->tags()) {
- if (!llvm::is_contained(OldAbiTagAttr->tags(), NewTag)) {
- Diag(NewAbiTagAttr->getLocation(),
- diag::err_new_abi_tag_on_redeclaration)
- << NewTag;
+ if (isa<NamespaceDecl>(New)) {
+ Decl *ComparedOld = Old->getCanonicalDecl();
+ if (const auto *NewAbiTagAttr = New->getAttr<AbiTagAttr>()) {
+ if (const auto *OldAbiTagAttr = ComparedOld->getAttr<AbiTagAttr>()) {
+ bool Diff = NewAbiTagAttr->tags_size() != OldAbiTagAttr->tags_size();
+ if (!Diff)
+ Diff = !llvm::all_of(NewAbiTagAttr->tags(), [OldAbiTagAttr](StringRef NewTag) {
+ return llvm::is_contained(OldAbiTagAttr->tags(), NewTag);
+ });
+ if (Diff) {
+ Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << llvm::join(NewAbiTagAttr->tags(), ", ") << true << llvm::join(OldAbiTagAttr->tags(), ", ");
Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
}
+ } else {
+ Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << llvm::join(NewAbiTagAttr->tags(), ", ") << false;
+ Diag(ComparedOld->getLocation(), diag::note_previous_declaration);
+ }
+ } else if (const auto *OldAbiTagAttr = ComparedOld->getAttr<AbiTagAttr>()) {
+ Diag(New->getLocation(), diag::warn_abi_tag_ignored_missing) << llvm::join(OldAbiTagAttr->tags(), ", ");
+ Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
+ }
+ } else {
+ // Re-declaration cannot add abi_tag's.
+ if (const auto *NewAbiTagAttr = New->getAttr<AbiTagAttr>()) {
+ if (const auto *OldAbiTagAttr = Old->getAttr<AbiTagAttr>()) {
+ for (const auto &NewTag : NewAbiTagAttr->tags()) {
+ if (!llvm::is_contained(OldAbiTagAttr->tags(), NewTag)) {
+ if (isa<NamespaceDecl>(New)) {
+ Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << NewTag << true << llvm::join(OldAbiTagAttr->tags(), ", ");
+ } else {
+ Diag(NewAbiTagAttr->getLocation(),
+ diag::err_new_abi_tag_on_redeclaration)
+ << NewTag;
+ }
+ Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
+ }
+ }
+ } else {
+ Diag(NewAbiTagAttr->getLocation(), diag::err_abi_tag_on_redeclaration);
+ Diag(Old->getLocation(), diag::note_previous_declaration);
}
- } else {
- Diag(NewAbiTagAttr->getLocation(), diag::err_abi_tag_on_redeclaration);
- Diag(Old->getLocation(), diag::note_previous_declaration);
}
}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3639640f2886a..e0c70ff07f447 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12102,9 +12102,10 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
AddPragmaAttributes(DeclRegionScope, Namespc);
ProcessAPINotes(Namespc);
- // FIXME: Should we be merging attributes?
if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
PushNamespaceVisibilityAttr(Attr, Loc);
+ if (PrevNS)
+ mergeDeclAttributes(Namespc, PrevNS);
if (IsStd)
StdNamespace = Namespc;
diff --git a/clang/test/SemaCXX/attr-abi-tag-syntax.cpp b/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
index 4f14a3c043b5c..2ed24c83cc0b4 100644
--- a/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
+++ b/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
@@ -17,7 +17,31 @@ inline namespace __attribute__((__abi_tag__)) {}
inline namespace N __attribute__((__abi_tag__)) {}
-} // namespcace N2
+} // namespace N2
+
+namespace N3 {
+inline namespace AbsentOld {}
+inline namespace AbsentOld __attribute__((__abi_tag__)) {}
+// expected-error at -1 {{'abi_tag' AbsentOld is ignored, applying no 'abi_tag'}}
+// expected-note at -3 {{previous declaration is here}}
+
+inline namespace AbsentNew __attribute__((__abi_tag__)) {}
+inline namespace AbsentNew {}
+// expected-error at -1 {{absent 'abi_tag' attribute is ignored, applying 'abi_tag' AbsentNew}}
+// expected-note at -3 {{previous declaration is here}}
+
+inline namespace Different __attribute__((abi_tag("A"))) {}
+inline namespace Different __attribute__((abi_tag("B"))) {}
+// expected-error at -1 {{'abi_tag' B is ignored, applying 'abi_tag' A}}
+// expected-note at -3 {{previous declaration is here}}
+inline namespace Different __attribute__((abi_tag("A"))) {}
+// No error as we compare with the canonical namespace decl, not with the previous one.
+
+inline namespace MultipleTags __attribute__((abi_tag("A", "B"))) {}
+inline namespace MultipleTags __attribute__((abi_tag("X", "Y", "B"))) {}
+// expected-error at -1 {{'abi_tag' B, X, Y is ignored, applying 'abi_tag' A, B}}
+// expected-note at -3 {{previous declaration is here}}
+} // namespace N3
__attribute__((abi_tag("B", "A"))) extern int a1;
>From b2df4615cdf6dd60d2f7a7fa5561b44d118ab227 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <vsapsai at apple.com>
Date: Thu, 3 Sep 2026 13:33:59 -0700
Subject: [PATCH 2/2] Fix formatting.
---
clang/lib/Sema/SemaDecl.cpp | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9f6949d853f09..93a2b11a9e3dc 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3376,19 +3376,25 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
if (const auto *OldAbiTagAttr = ComparedOld->getAttr<AbiTagAttr>()) {
bool Diff = NewAbiTagAttr->tags_size() != OldAbiTagAttr->tags_size();
if (!Diff)
- Diff = !llvm::all_of(NewAbiTagAttr->tags(), [OldAbiTagAttr](StringRef NewTag) {
- return llvm::is_contained(OldAbiTagAttr->tags(), NewTag);
- });
+ Diff = !llvm::all_of(
+ NewAbiTagAttr->tags(), [OldAbiTagAttr](StringRef NewTag) {
+ return llvm::is_contained(OldAbiTagAttr->tags(), NewTag);
+ });
if (Diff) {
- Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << llvm::join(NewAbiTagAttr->tags(), ", ") << true << llvm::join(OldAbiTagAttr->tags(), ", ");
+ Diag(NewAbiTagAttr->getLocation(),
+ diag::warn_abi_tag_ignored_different)
+ << llvm::join(NewAbiTagAttr->tags(), ", ") << true
+ << llvm::join(OldAbiTagAttr->tags(), ", ");
Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
}
} else {
- Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << llvm::join(NewAbiTagAttr->tags(), ", ") << false;
+ Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different)
+ << llvm::join(NewAbiTagAttr->tags(), ", ") << false;
Diag(ComparedOld->getLocation(), diag::note_previous_declaration);
}
} else if (const auto *OldAbiTagAttr = ComparedOld->getAttr<AbiTagAttr>()) {
- Diag(New->getLocation(), diag::warn_abi_tag_ignored_missing) << llvm::join(OldAbiTagAttr->tags(), ", ");
+ Diag(New->getLocation(), diag::warn_abi_tag_ignored_missing)
+ << llvm::join(OldAbiTagAttr->tags(), ", ");
Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
}
} else {
@@ -3398,11 +3404,13 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
for (const auto &NewTag : NewAbiTagAttr->tags()) {
if (!llvm::is_contained(OldAbiTagAttr->tags(), NewTag)) {
if (isa<NamespaceDecl>(New)) {
- Diag(NewAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored_different) << NewTag << true << llvm::join(OldAbiTagAttr->tags(), ", ");
+ Diag(NewAbiTagAttr->getLocation(),
+ diag::warn_abi_tag_ignored_different)
+ << NewTag << true << llvm::join(OldAbiTagAttr->tags(), ", ");
} else {
Diag(NewAbiTagAttr->getLocation(),
diag::err_new_abi_tag_on_redeclaration)
- << NewTag;
+ << NewTag;
}
Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
}
More information about the cfe-commits
mailing list