[clang] [clang][AST] Preserve dependent names in template diffs (PR #214989)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 8 10:03:53 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Matthias Görgens (matthiasgoergens)
<details>
<summary>Changes</summary>
`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
---
Full diff: https://github.com/llvm/llvm-project/pull/214989.diff
2 Files Affected:
- (modified) clang/lib/AST/ASTDiagnostic.cpp (+52-29)
- (added) clang/test/SemaTemplate/gh213068.cpp (+23)
``````````diff
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: [...]
``````````
</details>
https://github.com/llvm/llvm-project/pull/214989
More information about the cfe-commits
mailing list