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
