[clang] [Clang] Do not assume existing substition for template specialization (PR #227501)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 29 18:24:28 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/2] [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 b71758e4b9647e..e6d0cab0b50729 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 9b3589a527d034..507472db1e299a 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 2fca98faadb16e..0eb956d8965acd 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/2] 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 507472db1e299a..f8d7250d7279e1 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) {
More information about the cfe-commits
mailing list