https://github.com/matthiasgoergens created 
https://github.com/llvm/llvm-project/pull/214989

`TemplateDiff` stores template-template arguments as `TemplateDecl *`, but a 
dependent template name such as `PartialApply<B, F>::template R` has no 
declaration: `GetTemplateDecl` returned null for both sides, and 
`PrintTemplateTemplate` hit `assert((FromTD || ToTD))` as soon as a diagnostic 
printed a template tree containing such arguments — a crash on valid code.

Carry `TemplateName` through the diff tree instead. Equality uses 
`ASTContext::hasSameTemplateName`, which matches the previous 
canonical-declaration comparison for resolved names and additionally identifies 
equal dependent names; printing uses `TemplateName::print` when no declaration 
exists and is unchanged otherwise. The test pins the printed tree for two 
differing dependent names rather than merely the absence of the crash.

Fixes #213068.

## Tool use

Per the [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html): AI
tools were involved throughout the preparation of this change. I am the author
and accountable for the contribution.

Assisted-by: OpenAI Codex
Assisted-by: Claude Code
Assisted-by: Kimi
Assisted-by: DeepSeek


From 12f5d4b591132b1af8c2483724a97bf145c542ff Mon Sep 17 00:00:00 2001
From: Matthias Goergens <[email protected]>
Date: Sun, 2 Aug 2026 22:53:04 +0800
Subject: [PATCH] [clang][AST] Preserve dependent names in template diffs

Template-template argument diagnostics reduced TemplateName to TemplateDecl. 
Dependent template names have no declaration, so comparing two of them stored 
two null pointers and later asserted while printing.\n\nRetain TemplateName in 
the diagnostic tree, compare names through ASTContext, and print unresolved 
names directly. This both avoids the crash and reports the actual dependent 
arguments that differ.
---
 clang/lib/AST/ASTDiagnostic.cpp      | 81 ++++++++++++++++++----------
 clang/test/SemaTemplate/gh213068.cpp | 23 ++++++++
 2 files changed, 75 insertions(+), 29 deletions(-)
 create mode 100644 clang/test/SemaTemplate/gh213068.cpp

diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index f7888f58985db..0a76457cf4bb2 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -616,6 +616,7 @@ class TemplateDiff {
       bool IsValidInt = false;
       Expr *ArgExpr = nullptr;
       TemplateDecl *TD = nullptr;
+      TemplateName TN;
       ValueDecl *VD = nullptr;
       bool NeedAddressOf = false;
       bool IsNullPtr = false;
@@ -694,12 +695,12 @@ class TemplateDiff {
       SetDefault(FromDefault, ToDefault);
     }
 
-    void SetTemplateTemplateDiff(TemplateDecl *FromTD, TemplateDecl *ToTD,
+    void SetTemplateTemplateDiff(TemplateName FromTN, TemplateName ToTN,
                                  bool FromDefault, bool ToDefault) {
       assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty.");
       FlatTree[CurrentNode].Kind = TemplateTemplate;
-      FlatTree[CurrentNode].FromArgInfo.TD = FromTD;
-      FlatTree[CurrentNode].ToArgInfo.TD = ToTD;
+      FlatTree[CurrentNode].FromArgInfo.TN = FromTN;
+      FlatTree[CurrentNode].ToArgInfo.TN = ToTN;
       SetDefault(FromDefault, ToDefault);
     }
 
@@ -853,10 +854,10 @@ class TemplateDiff {
       ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr;
     }
 
-    void GetTemplateTemplateDiff(TemplateDecl *&FromTD, TemplateDecl *&ToTD) {
+    void GetTemplateTemplateDiff(TemplateName &FromTN, TemplateName &ToTN) {
       assert(FlatTree[ReadNode].Kind == TemplateTemplate && "Unexpected 
kind.");
-      FromTD = FlatTree[ReadNode].FromArgInfo.TD;
-      ToTD = FlatTree[ReadNode].ToArgInfo.TD;
+      FromTN = FlatTree[ReadNode].FromArgInfo.TN;
+      ToTN = FlatTree[ReadNode].ToArgInfo.TN;
     }
 
     void GetIntegerDiff(llvm::APSInt &FromInt, llvm::APSInt &ToInt,
@@ -1218,12 +1219,13 @@ class TemplateDiff {
   /// template template difference.
   void DiffTemplateTemplates(const TSTiterator &FromIter,
                              const TSTiterator &ToIter) {
-    TemplateDecl *FromDecl = GetTemplateDecl(FromIter);
-    TemplateDecl *ToDecl = GetTemplateDecl(ToIter);
-    Tree.SetTemplateTemplateDiff(FromDecl, ToDecl, FromIter.isEnd() && 
FromDecl,
-                                 ToIter.isEnd() && ToDecl);
-    Tree.SetSame(FromDecl && ToDecl &&
-                 FromDecl->getCanonicalDecl() == ToDecl->getCanonicalDecl());
+    TemplateName FromName = GetTemplateName(FromIter);
+    TemplateName ToName = GetTemplateName(ToIter);
+    Tree.SetTemplateTemplateDiff(FromName, ToName,
+                                 FromIter.isEnd() && !FromName.isNull(),
+                                 ToIter.isEnd() && !ToName.isNull());
+    Tree.SetSame(!FromName.isNull() && !ToName.isNull() &&
+                 Context.hasSameTemplateName(FromName, ToName));
   }
 
   /// InitializeNonTypeDiffVariables - Helper function for DiffNonTypes
@@ -1513,14 +1515,14 @@ class TemplateDiff {
     return QualType();
   }
 
-  /// GetTemplateDecl - Retrieves the template template arguments, including
+  /// GetTemplateName - Retrieves the template template arguments, including
   /// default arguments.
-  static TemplateDecl *GetTemplateDecl(const TSTiterator &Iter) {
+  static TemplateName GetTemplateName(const TSTiterator &Iter) {
     if (!Iter.isEnd())
-      return Iter->getAsTemplate().getAsTemplateDecl();
+      return Iter->getAsTemplateOrTemplatePattern();
     if (Iter.hasDesugaredTA())
-      return Iter.getDesugaredTA().getAsTemplate().getAsTemplateDecl();
-    return nullptr;
+      return Iter.getDesugaredTA().getAsTemplateOrTemplatePattern();
+    return {};
   }
 
   /// IsEqualExpr - Returns true if the expressions are the same in regards to
@@ -1571,9 +1573,9 @@ class TemplateDiff {
         return;
       }
       case DiffTree::TemplateTemplate: {
-        TemplateDecl *FromTD, *ToTD;
-        Tree.GetTemplateTemplateDiff(FromTD, ToTD);
-        PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(),
+        TemplateName FromTN, ToTN;
+        Tree.GetTemplateTemplateDiff(FromTN, ToTN);
+        PrintTemplateTemplate(FromTN, ToTN, Tree.FromDefault(),
                               Tree.ToDefault(), Tree.NodeIsSame());
         return;
       }
@@ -1801,20 +1803,41 @@ class TemplateDiff {
 
   /// PrintTemplateTemplate - Handles printing of template template arguments,
   /// highlighting argument differences.
-  void PrintTemplateTemplate(TemplateDecl *FromTD, TemplateDecl *ToTD,
+  void PrintTemplateTemplate(TemplateName FromTN, TemplateName ToTN,
                              bool FromDefault, bool ToDefault, bool Same) {
-    assert((FromTD || ToTD) && "Only one template argument may be missing.");
+    assert((!FromTN.isNull() || !ToTN.isNull()) &&
+           "Only one template argument may be missing.");
 
-    std::string FromName =
-        std::string(FromTD ? FromTD->getName() : "(no argument)");
-    std::string ToName = std::string(ToTD ? ToTD->getName() : "(no argument)");
-    if (FromTD && ToTD && FromName == ToName) {
-      FromName = FromTD->getQualifiedNameAsString();
-      ToName = ToTD->getQualifiedNameAsString();
+    auto GetName = [this](TemplateName TN, TemplateName::Qualified Qual) {
+      if (TN.isNull())
+        return std::string("(no argument)");
+      std::string Name;
+      llvm::raw_string_ostream Out(Name);
+      TN.print(Out, Policy, Qual);
+      return Name;
+    };
+    TemplateDecl *FromTD = FromTN.getAsTemplateDecl();
+    TemplateDecl *ToTD = ToTN.getAsTemplateDecl();
+    std::string FromName;
+    std::string ToName;
+    if (FromTD && ToTD) {
+      FromName = std::string(FromTD->getName());
+      ToName = std::string(ToTD->getName());
+      if (FromName == ToName) {
+        FromName = FromTD->getQualifiedNameAsString();
+        ToName = ToTD->getQualifiedNameAsString();
+      }
+    } else {
+      FromName = GetName(FromTN, TemplateName::Qualified::AsWritten);
+      ToName = GetName(ToTN, TemplateName::Qualified::AsWritten);
     }
 
     if (Same) {
-      OS << "template " << FromTD->getDeclName();
+      OS << "template ";
+      if (FromTD)
+        OS << FromTD->getDeclName();
+      else
+        FromTN.print(OS, Policy);
     } else if (!PrintTree) {
       OS << (FromDefault ? "(default) template " : "template ");
       Bold();
diff --git a/clang/test/SemaTemplate/gh213068.cpp 
b/clang/test/SemaTemplate/gh213068.cpp
new file mode 100644
index 0000000000000..8d6c89cbde850
--- /dev/null
+++ b/clang/test/SemaTemplate/gh213068.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: not %clang_cc1 -std=c++17 -fsyntax-only 
-fdiagnostics-show-template-tree %s 2>&1 | FileCheck %s
+
+template <template <template <typename> class, typename> class T,
+          template <typename> class V>
+struct PartialApply {
+  template <template <template <typename> class, typename> class A,
+            template <template <typename> class, typename> class B,
+            template <typename> class F, typename X>
+  using Mul = A<PartialApply<B, F>::template R, X>; // expected-note 
{{previous definition is here}}
+  template <template <template <typename> class, typename> class T_ffl,
+            template <typename> class V_ffl>
+  struct PartialApply_ffl {};
+  template <template <template <typename> class, typename> class A_ffl,
+            template <template <typename> class, typename> class B_ffl,
+            template <typename> class F_ffl, typename X>
+  using Mul = // expected-error {{type alias template redefinition with 
different types}}
+      A_ffl<PartialApply_ffl<B_ffl, F_ffl>::template R, X>;
+};
+
+// CHECK:      error: type alias template redefinition with different types
+// CHECK:      [template PartialApply_ffl<B_ffl, F_ffl>::template R != 
template PartialApply<B, F>::template R],
+// CHECK-NEXT: [...]

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

Reply via email to