[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