[clang] [Clang] Do not assume existing substition for template specialization (PR #227501)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 12 22:07:55 PDT 2026
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/227501
>From 8630009e9826c731dabcdc1bcb6136870762672a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 29 Sep 2026 17:27:05 -0500
Subject: [PATCH 1/3] [Clang] Do not assume existing substition for template
specialization
Mangled symbol is not always available when having template
specialization. For example, a template alias mangled the type when
actually doing substitution. In previous code, B in A<B>::foo() is
always mangled when reaching A<B>. When doing substition insides A<T>,
B is always available. Alias is not the case here, B is mangled when
reaching A<T> inside.
As now, TemplateName::SubstTemplateTemplateParm might needs to be
resolved further as B is not always defined, we remove it from the
switch case and find recursively like the origianl path.
Assisted-by: Claude # Tests, ReleaseNotes
---
clang/docs/ReleaseNotes.md | 4 +
clang/lib/AST/ItaniumMangle.cpp | 111 +++++++++++-----------
clang/test/CodeGenCXX/mangle-template.cpp | 15 +++
3 files changed, 77 insertions(+), 53 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b71758e4b9647..e6d0cab0b5072 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -806,6 +806,10 @@ features cannot lower the translation-unit ABI level;
- Fixed an assertion failure when a method or function definition follows an
Objective-C `@implementation` that was ended by a nested `@interface`,
`@protocol` or `@implementation` before its `@end`. (#GH209503)
+- Fixed an assertion failure in the Itanium mangler when a template template
+ parameter substituted through an alias template is used as the base of an
+ unresolved name, such as `TT<T>::value` in a non-type template parameter's
+ type. (#GH88592)
### OpenACC Specific Changes
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 9b3589a527d03..507472db1e299 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -462,8 +462,6 @@ class CXXNameMangler {
bool mangleSubstitution(TemplateName Template);
bool mangleSubstitution(uintptr_t Ptr);
- void mangleExistingSubstitution(TemplateName name);
-
bool mangleStandardSubstitution(const NamedDecl *ND);
void addSubstitution(const NamedDecl *ND) {
@@ -2590,57 +2588,70 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
const TemplateSpecializationType *TST =
cast<TemplateSpecializationType>(Ty);
TemplateName TN = TST->getTemplateName();
- switch (TN.getKind()) {
- case TemplateName::Template:
- case TemplateName::QualifiedTemplate: {
- TemplateDecl *TD = TN.getAsTemplateDecl();
-
- // If the base is a template template parameter, this is an
- // unresolved type.
- assert(TD && "no template for template specialization type");
- if (isa<TemplateTemplateParmDecl>(TD))
- goto unresolvedType;
-
- mangleSourceNameWithAbiTags(TD);
- break;
- }
- case TemplateName::DependentTemplate: {
- const DependentTemplateStorage *S = TN.getAsDependentTemplateName();
- mangleSourceName(S->getName().getIdentifier());
- break;
+
+ // It is possible that a name doesn't mangled when reaching a substitution
+ // like alias template.
+ bool MangledAsSubstitution = false;
+ while (TN.getKind() == TemplateName::SubstTemplateTemplateParm) {
+ TemplateName Replacement =
+ TN.getAsSubstTemplateTemplateParm()->getReplacement();
+ if (mangleSubstitution(Replacement)) {
+ MangledAsSubstitution = true;
+ break;
+ }
+ TN = Replacement;
}
- case TemplateName::OverloadedTemplate:
- case TemplateName::AssumedTemplate:
- case TemplateName::DeducedTemplate:
- llvm_unreachable("invalid base for a template specialization type");
+ if (!MangledAsSubstitution) {
+ switch (TN.getKind()) {
+ case TemplateName::Template:
+ case TemplateName::QualifiedTemplate: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
- case TemplateName::SubstTemplateTemplateParm: {
- SubstTemplateTemplateParmStorage *subst =
- TN.getAsSubstTemplateTemplateParm();
- mangleExistingSubstitution(subst->getReplacement());
- break;
- }
+ // If the base is a template template parameter, this is an
+ // unresolved type.
+ assert(TD && "no template for template specialization type");
+ if (isa<TemplateTemplateParmDecl>(TD))
+ goto unresolvedType;
- case TemplateName::SubstTemplateTemplateParmPack: {
- // FIXME: not clear how to mangle this!
- // template <template <class U> class T...> class A {
- // template <class U...> void foo(decltype(T<U>::foo) x...);
- // };
- Out << "_SUBSTPACK_";
- break;
- }
+ mangleSourceNameWithAbiTags(TD);
+ break;
+ }
+ case TemplateName::DependentTemplate: {
+ const DependentTemplateStorage *S = TN.getAsDependentTemplateName();
+ mangleSourceName(S->getName().getIdentifier());
+ break;
+ }
- case TemplateName::PackIndexingTemplate:
- DiagnoseUnsupportedPackIndexTemplateName();
- return false;
+ case TemplateName::OverloadedTemplate:
+ case TemplateName::AssumedTemplate:
+ case TemplateName::DeducedTemplate:
+ llvm_unreachable("invalid base for a template specialization type");
+
+ case TemplateName::SubstTemplateTemplateParm:
+ llvm_unreachable(
+ "substituted template template parameter handled above");
+
+ case TemplateName::SubstTemplateTemplateParmPack: {
+ // FIXME: not clear how to mangle this!
+ // template <template <class U> class T...> class A {
+ // template <class U...> void foo(decltype(T<U>::foo) x...);
+ // };
+ Out << "_SUBSTPACK_";
+ break;
+ }
- case TemplateName::UsingTemplate: {
- TemplateDecl *TD = TN.getAsTemplateDecl();
- assert(TD && !isa<TemplateTemplateParmDecl>(TD));
- mangleSourceNameWithAbiTags(TD);
- break;
- }
+ case TemplateName::PackIndexingTemplate:
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return false;
+
+ case TemplateName::UsingTemplate: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
+ assert(TD && !isa<TemplateTemplateParmDecl>(TD));
+ mangleSourceNameWithAbiTags(TD);
+ break;
+ }
+ }
}
// Note: we don't pass in the template name here. We are mangling the
@@ -7095,12 +7106,6 @@ void CXXNameMangler::mangleSeqID(unsigned SeqID) {
Out << '_';
}
-void CXXNameMangler::mangleExistingSubstitution(TemplateName tname) {
- bool result = mangleSubstitution(tname);
- assert(result && "no existing substitution for template name");
- (void) result;
-}
-
// <substitution> ::= S <seq-id> _
// ::= S_
bool CXXNameMangler::mangleSubstitution(const NamedDecl *ND) {
diff --git a/clang/test/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp
index 2fca98faadb16..0eb956d8965ac 100644
--- a/clang/test/CodeGenCXX/mangle-template.cpp
+++ b/clang/test/CodeGenCXX/mangle-template.cpp
@@ -433,3 +433,18 @@ namespace GH133610 {
}
// CHECK: @_ZN8GH1336101kINS_1CENS_1AIiEENS_1DEEEDtdtfp_sr1U1BIT1_EE3MEMET_T0_S5_
} // namespace GH133610
+
+namespace GH88592 {
+ template <bool> struct A;
+ template <typename> struct B { static constexpr bool value = false; };
+
+ template <class T, template <class> class TT>
+ using C = A<TT<T>::value>;
+
+ // The alias substitutes B for TT without B having been mangled earlier, so
+ // the substituted template template parameter can't be a substitution and
+ // must be mangled as the replacement template itself.
+ template <class T, C<T, B> * = nullptr> void f() {}
+ template void f<int>();
+ // CHECK: @_ZN7GH885921fIiTnPNS_1AIXsr1BIT_EE5valueEEELPNS1_ILb0EEE0EEEvv
+} // namespace GH88592
>From df0ef8eb8a7bbee80f932611c4ed55633f237835 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 29 Sep 2026 20:24:13 -0500
Subject: [PATCH 2/3] fixup! [Clang] Do not assume existing substition for
template specialization
---
clang/lib/AST/ItaniumMangle.cpp | 114 +++++++++++++++-----------------
1 file changed, 55 insertions(+), 59 deletions(-)
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 507472db1e299..f8d7250d7279e 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -462,6 +462,8 @@ class CXXNameMangler {
bool mangleSubstitution(TemplateName Template);
bool mangleSubstitution(uintptr_t Ptr);
+ TemplateName mangleExistingSubstitution(TemplateName name);
+
bool mangleStandardSubstitution(const NamedDecl *ND);
void addSubstitution(const NamedDecl *ND) {
@@ -2587,71 +2589,55 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::TemplateSpecialization: {
const TemplateSpecializationType *TST =
cast<TemplateSpecializationType>(Ty);
- TemplateName TN = TST->getTemplateName();
-
- // It is possible that a name doesn't mangled when reaching a substitution
- // like alias template.
- bool MangledAsSubstitution = false;
- while (TN.getKind() == TemplateName::SubstTemplateTemplateParm) {
- TemplateName Replacement =
- TN.getAsSubstTemplateTemplateParm()->getReplacement();
- if (mangleSubstitution(Replacement)) {
- MangledAsSubstitution = true;
- break;
- }
- TN = Replacement;
+ TemplateName TN = mangleExistingSubstitution(TST->getTemplateName());
+ switch (TN.getKind()) {
+ case TemplateName::Template:
+ case TemplateName::QualifiedTemplate: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
+
+ // If the base is a template template parameter, this is an
+ // unresolved type.
+ assert(TD && "no template for template specialization type");
+ if (isa<TemplateTemplateParmDecl>(TD))
+ goto unresolvedType;
+
+ mangleSourceNameWithAbiTags(TD);
+ break;
+ }
+ case TemplateName::DependentTemplate: {
+ const DependentTemplateStorage *S = TN.getAsDependentTemplateName();
+ mangleSourceName(S->getName().getIdentifier());
+ break;
}
- if (!MangledAsSubstitution) {
- switch (TN.getKind()) {
- case TemplateName::Template:
- case TemplateName::QualifiedTemplate: {
- TemplateDecl *TD = TN.getAsTemplateDecl();
-
- // If the base is a template template parameter, this is an
- // unresolved type.
- assert(TD && "no template for template specialization type");
- if (isa<TemplateTemplateParmDecl>(TD))
- goto unresolvedType;
+ case TemplateName::OverloadedTemplate:
+ case TemplateName::AssumedTemplate:
+ case TemplateName::DeducedTemplate:
+ llvm_unreachable("invalid base for a template specialization type");
- mangleSourceNameWithAbiTags(TD);
- break;
- }
- case TemplateName::DependentTemplate: {
- const DependentTemplateStorage *S = TN.getAsDependentTemplateName();
- mangleSourceName(S->getName().getIdentifier());
- break;
- }
+ case TemplateName::SubstTemplateTemplateParm:
+ // mangleExistingSubstitution already mangled a substitution for it.
+ break;
- case TemplateName::OverloadedTemplate:
- case TemplateName::AssumedTemplate:
- case TemplateName::DeducedTemplate:
- llvm_unreachable("invalid base for a template specialization type");
-
- case TemplateName::SubstTemplateTemplateParm:
- llvm_unreachable(
- "substituted template template parameter handled above");
-
- case TemplateName::SubstTemplateTemplateParmPack: {
- // FIXME: not clear how to mangle this!
- // template <template <class U> class T...> class A {
- // template <class U...> void foo(decltype(T<U>::foo) x...);
- // };
- Out << "_SUBSTPACK_";
- break;
- }
+ case TemplateName::SubstTemplateTemplateParmPack: {
+ // FIXME: not clear how to mangle this!
+ // template <template <class U> class T...> class A {
+ // template <class U...> void foo(decltype(T<U>::foo) x...);
+ // };
+ Out << "_SUBSTPACK_";
+ break;
+ }
- case TemplateName::PackIndexingTemplate:
- DiagnoseUnsupportedPackIndexTemplateName();
- return false;
+ case TemplateName::PackIndexingTemplate:
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return false;
- case TemplateName::UsingTemplate: {
- TemplateDecl *TD = TN.getAsTemplateDecl();
- assert(TD && !isa<TemplateTemplateParmDecl>(TD));
- mangleSourceNameWithAbiTags(TD);
- break;
- }
- }
+ case TemplateName::UsingTemplate: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
+ assert(TD && !isa<TemplateTemplateParmDecl>(TD));
+ mangleSourceNameWithAbiTags(TD);
+ break;
+ }
}
// Note: we don't pass in the template name here. We are mangling the
@@ -7106,6 +7092,16 @@ void CXXNameMangler::mangleSeqID(unsigned SeqID) {
Out << '_';
}
+TemplateName CXXNameMangler::mangleExistingSubstitution(TemplateName tname) {
+ while (SubstTemplateTemplateParmStorage *subst =
+ tname.getAsSubstTemplateTemplateParm()) {
+ if (mangleSubstitution(subst->getReplacement()))
+ return tname;
+ tname = subst->getReplacement();
+ }
+ return tname;
+}
+
// <substitution> ::= S <seq-id> _
// ::= S_
bool CXXNameMangler::mangleSubstitution(const NamedDecl *ND) {
>From 6c43b4fcd7b6dca088dd4047c958d7c8504e0100 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 13 Oct 2026 00:07:39 -0500
Subject: [PATCH 3/3] fixup! [Clang] Do not assume existing substition for
template specialization
---
clang/docs/ReleaseNotes.md | 8 ++++++--
clang/lib/AST/ItaniumMangle.cpp | 2 ++
...e-subst-template-template-parm-gh88592.cpp | 20 +++++++++++++++++++
clang/test/CodeGenCXX/mangle-template.cpp | 15 --------------
4 files changed, 28 insertions(+), 17 deletions(-)
create mode 100644 clang/test/CodeGenCXX/mangle-subst-template-template-parm-gh88592.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3257e5894c2e1..9d200294530c5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -64,6 +64,11 @@ in a future version of Clang.
### ABI Changes in This Version
+- Clang now mangles a template template parameter that was substituted through
+ an alias template and is used as the base of an unresolved name, such as
+ `TT<T>::value` in a non-type template parameter's type, as the substituted
+ template. Previous versions emitted an ill-formed symbol with no name in that
+ position. `-fclang-abi-compat=23` restores the previous behavior. (#GH88592)
- Except on PlayStation, Clang now derives the x86-64 System V AVX ABI level
for 256- and 512-bit vector arguments and returns from effective per-function
target features. Features and `arch=` CPUs that imply AVX or AVX512F are
@@ -999,12 +1004,11 @@ features cannot lower the translation-unit ABI level;
- Fixed an assertion failure when a method or function definition follows an
Objective-C `@implementation` that was ended by a nested `@interface`,
`@protocol` or `@implementation` before its `@end`. (#GH209503)
+- Fixed use-after-free with annotate attribute on a C++ class method with a this-adjusting thunk.
- Fixed an assertion failure in the Itanium mangler when a template template
parameter substituted through an alias template is used as the base of an
unresolved name, such as `TT<T>::value` in a non-type template parameter's
type. (#GH88592)
-- Fixed use-after-free with annotate attribute on a C++ class method with a this-adjusting thunk.
->>>>>>> main
### OpenACC Specific Changes
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2150eb3ccfc77..3485825e30327 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -7173,6 +7173,8 @@ TemplateName CXXNameMangler::mangleExistingSubstitution(TemplateName tname) {
tname.getAsSubstTemplateTemplateParm()) {
if (mangleSubstitution(subst->getReplacement()))
return tname;
+ if (isCompatibleWith(LangOptions::ClangABI::Ver23))
+ return tname;
tname = subst->getReplacement();
}
return tname;
diff --git a/clang/test/CodeGenCXX/mangle-subst-template-template-parm-gh88592.cpp b/clang/test/CodeGenCXX/mangle-subst-template-template-parm-gh88592.cpp
new file mode 100644
index 0000000000000..2fd34d58d0ba7
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-subst-template-template-parm-gh88592.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++20 -fclang-abi-compat=latest -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -fclang-abi-compat=23 -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck --check-prefix=ABICOMPAT23 %s
+
+namespace GH88592 {
+ template <bool> struct A;
+ template <typename> struct B { static constexpr bool value = false; };
+
+ template <class T, template <class> class TT>
+ using C = A<TT<T>::value>;
+
+ // The alias substitutes B for TT without B having been mangled earlier, so
+ // the substituted template template parameter can't be a substitution and
+ // must be mangled as the replacement template itself.
+ template <class T, C<T, B> * = nullptr> void f() {}
+ template void f<int>();
+ // CHECK: @_ZN7GH885921fIiTnPNS_1AIXsr1BIT_EE5valueEEELPNS1_ILb0EEE0EEEvv
+ // Clang 23 and before mangled nothing for the template name, producing an
+ // ill-formed symbol.
+ // ABICOMPAT23: @_ZN7GH885921fIiTnPNS_1AIXsrIT_EE5valueEEELPNS1_ILb0EEE0EEEvv
+} // namespace GH88592
diff --git a/clang/test/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp
index 0eb956d8965ac..2fca98faadb16 100644
--- a/clang/test/CodeGenCXX/mangle-template.cpp
+++ b/clang/test/CodeGenCXX/mangle-template.cpp
@@ -433,18 +433,3 @@ namespace GH133610 {
}
// CHECK: @_ZN8GH1336101kINS_1CENS_1AIiEENS_1DEEEDtdtfp_sr1U1BIT1_EE3MEMET_T0_S5_
} // namespace GH133610
-
-namespace GH88592 {
- template <bool> struct A;
- template <typename> struct B { static constexpr bool value = false; };
-
- template <class T, template <class> class TT>
- using C = A<TT<T>::value>;
-
- // The alias substitutes B for TT without B having been mangled earlier, so
- // the substituted template template parameter can't be a substitution and
- // must be mangled as the replacement template itself.
- template <class T, C<T, B> * = nullptr> void f() {}
- template void f<int>();
- // CHECK: @_ZN7GH885921fIiTnPNS_1AIXsr1BIT_EE5valueEEELPNS1_ILb0EEE0EEEvv
-} // namespace GH88592
More information about the cfe-commits
mailing list