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 <[email protected]>
Date: Wed, 2 Sep 2026 23:52:33 -0700
Subject: [PATCH 1/8] [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 a1f24a8caedae5..a5f8fb5a971f33 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 1da7698944b24b..9435578145ddf2 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 fca68f292f6671..82911fa7a96019 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 e52f99b4cd98db..9f6949d853f09a 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 3639640f2886ab..e0c70ff07f447c 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 4f14a3c043b5cf..2ed24c83cc0b4b 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@-1 {{'abi_tag' AbsentOld is ignored, applying no 'abi_tag'}}
+// expected-note@-3 {{previous declaration is here}}
+
+inline namespace AbsentNew __attribute__((__abi_tag__)) {}
+inline namespace AbsentNew {}
+// expected-error@-1 {{absent 'abi_tag' attribute is ignored, applying 
'abi_tag' AbsentNew}}
+// expected-note@-3 {{previous declaration is here}}
+
+inline namespace Different __attribute__((abi_tag("A"))) {}
+inline namespace Different __attribute__((abi_tag("B"))) {}
+// expected-error@-1 {{'abi_tag' B is ignored, applying 'abi_tag' A}}
+// expected-note@-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@-1 {{'abi_tag' B, X, Y is ignored, applying 'abi_tag' A, B}}
+// expected-note@-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 <[email protected]>
Date: Thu, 3 Sep 2026 13:33:59 -0700
Subject: [PATCH 2/8] 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 9f6949d853f09a..93a2b11a9e3dc1 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);
           }

>From 323ec0b41afe9c6cc931ded69b1fe8971b30edfa Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Mon, 28 Sep 2026 15:21:04 -0700
Subject: [PATCH 3/8] Address review comments.

---
 clang/include/clang/Basic/AttrDocs.td         |  9 ++++++
 clang/include/clang/Basic/DiagnosticGroups.td |  2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  7 ++---
 clang/lib/Sema/SemaDecl.cpp                   | 30 +++++++------------
 clang/test/SemaCXX/attr-abi-tag-syntax.cpp    | 15 +++++-----
 5 files changed, 29 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 0a8977d3c6b14f..b2bc8ace9ca5ae 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6917,6 +6917,15 @@ could have a different set of data members and thus have 
a different size. Using
 the `abi_tag` attribute, it is possible to have different mangled names for
 a global variable of the class type. Therefore, the old code could keep using
 the old mangled name and the new code will use the new mangled name with tags.
+
+On an inline namespace, the attribute does not change the namespace's mangled
+name. Instead, the tags are added to functions and variables whose type refers
+to an entity of that namespace, unless the tags already appear in the mangled
+name. As in GCC, the attribute is not limited to the namespace where it is
+written but affects subsequent reopenings. Specifying different tags on a
+reopening can make Clang and GCC produce different mangled names. So, for
+portability, it is recommended to use the same `abi_tag` on each namespace
+reopening.
   }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 9435578145ddf2..136cadc9798245 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1868,7 +1868,7 @@ def UnknownArgument : DiagGroup<"unknown-argument">;
 
 def UnsupportedABI : DiagGroup<"unsupported-abi">;
 
-def IgnoredAbiTagAttr : DiagGroup<"abi-tag">;
+def IgnoredAbiTagAttr : DiagGroup<"abi-tag-redeclaration">;
 
 // 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.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 903d8310ed8650..dedc1d5cea3c2a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6486,12 +6486,9 @@ 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;
+  "%select{no 'abi_tag'|'abi_tag' %1}0 prevents applying 'abi_tag' %2 later">,
+  InGroup<IgnoredAbiTagAttr>;
 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 11de40ef15d740..4b6985e210394b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3381,21 +3381,17 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl 
*Old,
                 return llvm::is_contained(OldAbiTagAttr->tags(), NewTag);
               });
         if (Diff) {
-          Diag(NewAbiTagAttr->getLocation(),
+          Diag(OldAbiTagAttr->getLocation(),
                diag::warn_abi_tag_ignored_different)
-              << llvm::join(NewAbiTagAttr->tags(), ", ") << true
-              << llvm::join(OldAbiTagAttr->tags(), ", ");
-          Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
+              << true << llvm::join(OldAbiTagAttr->tags(), ", ")
+              << llvm::join(NewAbiTagAttr->tags(), ", ");
+          Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
         }
       } else {
-        Diag(NewAbiTagAttr->getLocation(), 
diag::warn_abi_tag_ignored_different)
-            << llvm::join(NewAbiTagAttr->tags(), ", ") << false;
-        Diag(ComparedOld->getLocation(), diag::note_previous_declaration);
+        Diag(ComparedOld->getLocation(), diag::warn_abi_tag_ignored_different)
+            << false << "" << llvm::join(NewAbiTagAttr->tags(), ", ");
+        Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
       }
-    } 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.
@@ -3403,15 +3399,9 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
       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(NewAbiTagAttr->getLocation(),
+                 diag::err_new_abi_tag_on_redeclaration)
+                << NewTag;
             Diag(OldAbiTagAttr->getLocation(), 
diag::note_previous_declaration);
           }
         }
diff --git a/clang/test/SemaCXX/attr-abi-tag-syntax.cpp 
b/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
index 2ed24c83cc0b4b..1bf20f4d8cbed2 100644
--- a/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
+++ b/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
@@ -22,25 +22,24 @@ inline namespace N __attribute__((__abi_tag__)) {}
 namespace N3 {
 inline namespace AbsentOld {}
 inline namespace AbsentOld __attribute__((__abi_tag__)) {}
-// expected-error@-1 {{'abi_tag' AbsentOld is ignored, applying no 'abi_tag'}}
-// expected-note@-3 {{previous declaration is here}}
+// expected-warning@-2 {{no 'abi_tag' prevents applying 'abi_tag' AbsentOld 
later}}
+// expected-note@-2 {{declared here}}
 
 inline namespace AbsentNew __attribute__((__abi_tag__)) {}
 inline namespace AbsentNew {}
-// expected-error@-1 {{absent 'abi_tag' attribute is ignored, applying 
'abi_tag' AbsentNew}}
-// expected-note@-3 {{previous declaration is here}}
+// No tags on a namespace reopening can be deliberate, no diagnostic.
 
 inline namespace Different __attribute__((abi_tag("A"))) {}
 inline namespace Different __attribute__((abi_tag("B"))) {}
-// expected-error@-1 {{'abi_tag' B is ignored, applying 'abi_tag' A}}
-// expected-note@-3 {{previous declaration is here}}
+// expected-warning@-2 {{'abi_tag' A prevents applying 'abi_tag' B later}}
+// expected-note@-2 {{declared 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@-1 {{'abi_tag' B, X, Y is ignored, applying 'abi_tag' A, B}}
-// expected-note@-3 {{previous declaration is here}}
+// expected-warning@-2 {{'abi_tag' A, B prevents applying 'abi_tag' B, X, Y 
later}}
+// expected-note@-2 {{declared here}}
 } // namespace N3
 
 __attribute__((abi_tag("B", "A"))) extern int a1;

>From 2bed60a85f2103a7af980ac5f6ba954401916bfb Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Tue, 29 Sep 2026 09:30:51 -0700
Subject: [PATCH 4/8] Update release notes to reflect the latest code changes.

---
 clang/docs/ReleaseNotes.md | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 5766f764dc788b..ea78dd918b2b77 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -524,10 +524,8 @@ 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`.
+- Added `-Wabi-tag-redeclaration` to diagnose when a forward namespace
+  declaration prevents applying `abi_tag` later on namespace reopening.
 
 ### Improvements to Clang's time-trace
 

>From 1d53da161003817111ea14925b4f99ff25bf242d Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Thu, 1 Oct 2026 10:30:50 -0700
Subject: [PATCH 5/8] Update clang/include/clang/Basic/DiagnosticSemaKinds.td

Co-authored-by: Aaron Ballman <[email protected]>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dedc1d5cea3c2a..95fb464cb96e62 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6488,7 +6488,7 @@ def warn_attr_abi_tag_namespace : Warning<
   InGroup<IgnoredAttributes>;
 def warn_abi_tag_ignored_different : Warning<
   "%select{no 'abi_tag'|'abi_tag' %1}0 prevents applying 'abi_tag' %2 later">,
-  InGroup<IgnoredAbiTagAttr>;
+  InGroup<DiagGroup<"abi-tag-redeclaration">>;
 def err_abi_tag_on_redeclaration : Error<
   "cannot add 'abi_tag' attribute in a redeclaration">;
 def err_new_abi_tag_on_redeclaration : Error<

>From cd3bfeb6819bc91ebdeea5d87888fab93796fc23 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Thu, 1 Oct 2026 10:33:46 -0700
Subject: [PATCH 6/8] Update clang/include/clang/Basic/DiagnosticSemaKinds.td

Co-authored-by: Aaron Ballman <[email protected]>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 95fb464cb96e62..08dfa6575b7e82 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6487,7 +6487,7 @@ 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_different : Warning<
-  "%select{no 'abi_tag'|'abi_tag' %1}0 prevents applying 'abi_tag' %2 later">,
+  "%select{no 'abi_tag'|'abi_tag(\"%1\")'}0 prevents applying 
'abi_tag(\"%2\")' later">,
   InGroup<DiagGroup<"abi-tag-redeclaration">>;
 def err_abi_tag_on_redeclaration : Error<
   "cannot add 'abi_tag' attribute in a redeclaration">;

>From ddb0f22c7588b0f6c10c442ba9806fe776ffe744 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Thu, 1 Oct 2026 11:29:17 -0700
Subject: [PATCH 7/8] Address review comments, don't merge namespace
 attributes.

---
 clang/include/clang/Basic/DiagnosticGroups.td |  2 -
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/lib/Sema/SemaDecl.cpp                   | 61 ++++++-------------
 clang/lib/Sema/SemaDeclCXX.cpp                | 28 ++++++++-
 clang/test/SemaCXX/attr-abi-tag-syntax.cpp    |  9 ++-
 5 files changed, 50 insertions(+), 52 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 136cadc9798245..1da7698944b24b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1868,8 +1868,6 @@ def UnknownArgument : DiagGroup<"unknown-argument">;
 
 def UnsupportedABI : DiagGroup<"unsupported-abi">;
 
-def IgnoredAbiTagAttr : DiagGroup<"abi-tag-redeclaration">;
-
 // 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 08dfa6575b7e82..c4686ae01f83f6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6486,7 +6486,7 @@ 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_different : Warning<
+def warn_abi_tag_ignored : Warning<
   "%select{no 'abi_tag'|'abi_tag(\"%1\")'}0 prevents applying 
'abi_tag(\"%2\")' later">,
   InGroup<DiagGroup<"abi-tag-redeclaration">>;
 def err_abi_tag_on_redeclaration : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4b6985e210394b..db5e66cb96c3ec 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2961,13 +2961,11 @@ 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)) {
-    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))
+  } 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))
     NewAttr = S.mergeDLLImportAttr(D, *ImportA);
   else if (const auto *ExportA = dyn_cast<DLLExportAttr>(Attr))
     NewAttr = S.mergeDLLExportAttr(D, *ExportA);
@@ -3370,45 +3368,20 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl 
*Old,
     }
   }
 
-  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(OldAbiTagAttr->getLocation(),
-               diag::warn_abi_tag_ignored_different)
-              << true << llvm::join(OldAbiTagAttr->tags(), ", ")
-              << llvm::join(NewAbiTagAttr->tags(), ", ");
-          Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
+  // 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;
+          Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration);
         }
-      } else {
-        Diag(ComparedOld->getLocation(), diag::warn_abi_tag_ignored_different)
-            << false << "" << llvm::join(NewAbiTagAttr->tags(), ", ");
-        Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
-      }
-    }
-  } 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)) {
-            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 f1ffdcef920b4c..9c3c530e9a86dd 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12106,10 +12106,34 @@ 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 (Decl *PrevTagD = Namespc->getCanonicalDecl(); PrevTagD != Namespc) {
+    auto PrintableTags = [](ArrayRef<StringRef> Tags) {
+      return llvm::join(Tags, R"(", ")");
+    };
+    if (const auto *NewAbiTagAttr = Namespc->getAttr<AbiTagAttr>()) {
+      if (const auto *OldAbiTagAttr = PrevTagD->getAttr<AbiTagAttr>()) {
+        SmallVector<StringRef, 4> ExtraTags;
+        llvm::copy_if(NewAbiTagAttr->tags(), std::back_inserter(ExtraTags),
+                      [OldAbiTagAttr](StringRef NewTag) {
+                        return !llvm::is_contained(OldAbiTagAttr->tags(),
+                                                   NewTag);
+                      });
+        if (!ExtraTags.empty()) {
+          Diag(OldAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored)
+              << true << PrintableTags(OldAbiTagAttr->tags())
+              << PrintableTags(ExtraTags);
+          Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
+        }
+      } else {
+        Diag(PrevTagD->getLocation(), diag::warn_abi_tag_ignored)
+            << false << "" << PrintableTags(NewAbiTagAttr->tags());
+        Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
+      }
+    }
+  }
 
   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 1bf20f4d8cbed2..d0de37c1efde1d 100644
--- a/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
+++ b/clang/test/SemaCXX/attr-abi-tag-syntax.cpp
@@ -22,7 +22,7 @@ inline namespace N __attribute__((__abi_tag__)) {}
 namespace N3 {
 inline namespace AbsentOld {}
 inline namespace AbsentOld __attribute__((__abi_tag__)) {}
-// expected-warning@-2 {{no 'abi_tag' prevents applying 'abi_tag' AbsentOld 
later}}
+// expected-warning@-2 {{no 'abi_tag' prevents applying 'abi_tag("AbsentOld")' 
later}}
 // expected-note@-2 {{declared here}}
 
 inline namespace AbsentNew __attribute__((__abi_tag__)) {}
@@ -31,15 +31,18 @@ inline namespace AbsentNew {}
 
 inline namespace Different __attribute__((abi_tag("A"))) {}
 inline namespace Different __attribute__((abi_tag("B"))) {}
-// expected-warning@-2 {{'abi_tag' A prevents applying 'abi_tag' B later}}
+// expected-warning@-2 {{'abi_tag("A")' prevents applying 'abi_tag("B")' 
later}}
 // expected-note@-2 {{declared 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-warning@-2 {{'abi_tag' A, B prevents applying 'abi_tag' B, X, Y 
later}}
+// expected-warning@-2 {{'abi_tag("A", "B")' prevents applying 'abi_tag("X", 
"Y")' later}}
 // expected-note@-2 {{declared here}}
+inline namespace MultipleTagsSubset __attribute__((abi_tag("A", "B"))) {}
+inline namespace MultipleTagsSubset __attribute__((abi_tag("A"))) {}
+// No diagnostic as tag "A" *is* applied.
 } // namespace N3
 
 __attribute__((abi_tag("B", "A"))) extern int a1;

>From c860d990967a32a92f456bc2139e975b534faa31 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <[email protected]>
Date: Fri, 9 Oct 2026 15:54:03 -0700
Subject: [PATCH 8/8] Move ABI tag diagnostics to a separate function.

---
 clang/lib/Sema/SemaDeclCXX.cpp | 56 +++++++++++++++++++---------------
 1 file changed, 31 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f2ef8fe678c274..5087836eae6141 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12000,6 +12000,35 @@ static void DiagnoseNamespaceInlineMismatch(Sema &S, 
SourceLocation KeywordLoc,
   *IsInline = PrevNS->isInline();
 }
 
+/// Diagnose 'abi_tag' on a namespace rejecting a different tag on a subsequent
+/// namespace re-opening.
+static void DiagnoseNamespaceABITagMismatch(Sema &S, const Decl *NewD,
+                                            const Decl *OldD) {
+  assert(NewD != OldD);
+  auto PrintableTags = [](ArrayRef<StringRef> Tags) {
+    return llvm::join(Tags, R"(", ")");
+  };
+  if (const auto *NewAbiTagAttr = NewD->getAttr<AbiTagAttr>()) {
+    if (const auto *OldAbiTagAttr = OldD->getAttr<AbiTagAttr>()) {
+      SmallVector<StringRef, 4> ExtraTags;
+      llvm::copy_if(NewAbiTagAttr->tags(), std::back_inserter(ExtraTags),
+                    [OldAbiTagAttr](StringRef NewTag) {
+                      return !llvm::is_contained(OldAbiTagAttr->tags(), 
NewTag);
+                    });
+      if (!ExtraTags.empty()) {
+        S.Diag(OldAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored)
+            << true << PrintableTags(OldAbiTagAttr->tags())
+            << PrintableTags(ExtraTags);
+        S.Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
+      }
+    } else {
+      S.Diag(OldD->getLocation(), diag::warn_abi_tag_ignored)
+          << false << "" << PrintableTags(NewAbiTagAttr->tags());
+      S.Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
+    }
+  }
+}
+
 /// ActOnStartNamespaceDef - This is called at the start of a namespace
 /// definition.
 Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
@@ -12109,31 +12138,8 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
   // FIXME: Should we be merging attributes?
   if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
     PushNamespaceVisibilityAttr(Attr, Loc);
-  if (Decl *PrevTagD = Namespc->getCanonicalDecl(); PrevTagD != Namespc) {
-    auto PrintableTags = [](ArrayRef<StringRef> Tags) {
-      return llvm::join(Tags, R"(", ")");
-    };
-    if (const auto *NewAbiTagAttr = Namespc->getAttr<AbiTagAttr>()) {
-      if (const auto *OldAbiTagAttr = PrevTagD->getAttr<AbiTagAttr>()) {
-        SmallVector<StringRef, 4> ExtraTags;
-        llvm::copy_if(NewAbiTagAttr->tags(), std::back_inserter(ExtraTags),
-                      [OldAbiTagAttr](StringRef NewTag) {
-                        return !llvm::is_contained(OldAbiTagAttr->tags(),
-                                                   NewTag);
-                      });
-        if (!ExtraTags.empty()) {
-          Diag(OldAbiTagAttr->getLocation(), diag::warn_abi_tag_ignored)
-              << true << PrintableTags(OldAbiTagAttr->tags())
-              << PrintableTags(ExtraTags);
-          Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
-        }
-      } else {
-        Diag(PrevTagD->getLocation(), diag::warn_abi_tag_ignored)
-            << false << "" << PrintableTags(NewAbiTagAttr->tags());
-        Diag(NewAbiTagAttr->getLocation(), diag::note_declared_at);
-      }
-    }
-  }
+  if (Decl *PrevTagDecl = Namespc->getCanonicalDecl(); PrevTagDecl != Namespc)
+    DiagnoseNamespaceABITagMismatch(*this, Namespc, PrevTagDecl);
 
   if (IsStd)
     StdNamespace = Namespc;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to