https://github.com/kimgr created 
https://github.com/llvm/llvm-project/pull/216570

Printing function template instantiations with parameter packs would yield 
confusing results where deduced arguments would be nested in an additional set 
of angle-brackets to signify the pack:

    template <typename ...Args> void f(Args &&...);
    void t() {
        f(10, 'a');
    }

    // printing the f instantiation gives:
    template<> void f<<int, char>>(int &&, char &&);

Remove the special-case template argument printing from DeclPrinter, and 
delegate to the type printer's printTemplateArgumentList overloads instead.

To avoid confusion in the face of multiple packs, treat packs similar to 
default arguments with SuppressDefaultTemplateArgs and drop all template 
arguments after an unexpanded pack. Any trailing arguments (including other 
packs) must have been deduced from arguments we can already see.

While here, remove an unused copy of the Args left over from a cleanup in 
3d7dcec5db2f8.

Fixes #211737.

From 50e091dc4b3dcba22d17b321244f74a500652eec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kim=20Gr=C3=A4sman?= <[email protected]>
Date: Sun, 16 Aug 2026 14:48:13 +0200
Subject: [PATCH] Drop template arguments after unexpanded packs when printing

Printing function template instantiations with parameter packs would
yield confusing results where deduced arguments would be nested in an
additional set of angle-brackets to signify the pack:

    template <typename ...Args> void f(Args &&...);
    void t() {
        f(10, 'a');
    }

    // printing the f instantiation gives:
    template<> void f<<int, char>>(int &&, char &&);

Remove the special-case template argument printing from DeclPrinter, and
delegate to the type printer's printTemplateArgumentList overloads
instead.

To avoid confusion in the face of multiple packs, treat packs similar to
default arguments with SuppressDefaultTemplateArgs and drop all template
arguments after an unexpanded pack. Any trailing arguments (including
other packs) must have been deduced from arguments we can already see.

While here, remove an unused copy of the Args left over from a cleanup
in 3d7dcec5db2f8.

Fixes #211737.
---
 clang/lib/AST/DeclPrinter.cpp           | 48 ++++---------------------
 clang/lib/AST/TypePrinter.cpp           | 26 +++++++++-----
 clang/unittests/AST/DeclPrinterTest.cpp | 28 +++++++++++++++
 3 files changed, 52 insertions(+), 50 deletions(-)

diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 4be3e977b815e..89835ae5103a9 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -122,10 +122,6 @@ namespace {
 
     void printTemplateParameters(const TemplateParameterList *Params,
                                  bool OmitTemplateKW = false);
-    void printTemplateArguments(ArrayRef<TemplateArgument> Args,
-                                const TemplateParameterList *Params);
-    void printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
-                                const TemplateParameterList *Params);
     enum class AttrPosAsWritten { Default = 0, Left, Right };
     std::optional<std::string>
     prettyPrintAttributes(const Decl *D,
@@ -728,13 +724,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
     Proto = GuideDecl->getDeducedTemplate()->getDeclName().getAsString();
   if (D->isFunctionTemplateSpecialization()) {
     llvm::raw_string_ostream POut(Proto);
-    DeclPrinter TArgPrinter(POut, SubPolicy, Context, Indentation);
     const auto *TArgAsWritten = D->getTemplateSpecializationArgsAsWritten();
     if (TArgAsWritten && !Policy.PrintAsCanonical)
-      TArgPrinter.printTemplateArguments(TArgAsWritten->arguments(), nullptr);
+      printTemplateArgumentList(POut, TArgAsWritten->arguments(), SubPolicy);
     else if (const TemplateArgumentList *TArgs =
                  D->getTemplateSpecializationArgs())
-      TArgPrinter.printTemplateArguments(TArgs->asArray(), nullptr);
+      printTemplateArgumentList(POut, TArgs->asArray(), SubPolicy);
   }
 
   QualType Ty = D->getType();
@@ -1115,9 +1110,11 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
       const ASTTemplateArgumentListInfo *TArgAsWritten =
           S->getTemplateArgsAsWritten();
       if (TArgAsWritten && !Policy.PrintAsCanonical)
-        printTemplateArguments(TArgAsWritten->arguments(), TParams);
+        printTemplateArgumentList(Out, TArgAsWritten->arguments(), Policy,
+                                  TParams);
       else
-        printTemplateArguments(S->getTemplateArgs().asArray(), TParams);
+        printTemplateArgumentList(Out, S->getTemplateArgs().asArray(), Policy,
+                                  TParams);
     }
   }
 
@@ -1225,39 +1222,6 @@ void DeclPrinter::printTemplateParameters(const 
TemplateParameterList *Params,
     Out << ' ';
 }
 
-void DeclPrinter::printTemplateArguments(ArrayRef<TemplateArgument> Args,
-                                         const TemplateParameterList *Params) {
-  Out << "<";
-  for (size_t I = 0, E = Args.size(); I < E; ++I) {
-    if (I)
-      Out << ", ";
-    if (!Params)
-      Args[I].print(Policy, Out, /*IncludeType*/ true);
-    else
-      Args[I].print(Policy, Out,
-                    TemplateParameterList::shouldIncludeTypeForArgument(
-                        Policy, Params, I));
-  }
-  Out << ">";
-}
-
-void DeclPrinter::printTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
-                                         const TemplateParameterList *Params) {
-  Out << "<";
-  for (size_t I = 0, E = Args.size(); I < E; ++I) {
-    if (I)
-      Out << ", ";
-    if (!Params)
-      Args[I].getArgument().print(Policy, Out, /*IncludeType*/ true);
-    else
-      Args[I].getArgument().print(
-          Policy, Out,
-          TemplateParameterList::shouldIncludeTypeForArgument(Policy, Params,
-                                                              I));
-  }
-  Out << ">";
-}
-
 void DeclPrinter::VisitTemplateDecl(const TemplateDecl *D) {
   printTemplateParameters(D->getTemplateParameters());
 
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 20dd450d5d63f..7bf8b1900d0b1 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2564,14 +2564,24 @@ template <typename TA>
 static void
 printTo(raw_ostream &OS, ArrayRef<TA> Args, const PrintingPolicy &Policy,
         const TemplateParameterList *TPL, bool IsPack, unsigned ParmIndex) {
-  // Drop trailing template arguments that match default arguments.
-  if (TPL && Policy.SuppressDefaultTemplateArgs && !Policy.PrintAsCanonical &&
-      !Args.empty() && !IsPack && Args.size() <= TPL->size()) {
-    llvm::SmallVector<TemplateArgument, 8> OrigArgs;
-    for (const TA &A : Args)
-      OrigArgs.push_back(getArgument(A));
-    while (!Args.empty() && getArgument(Args.back()).getIsDefaulted())
-      Args = Args.drop_back();
+  if (Policy.SuppressDefaultTemplateArgs && !Policy.PrintAsCanonical) {
+    // Drop trailing template arguments that match default arguments.
+    if (TPL && !Args.empty() && !IsPack && Args.size() <= TPL->size()) {
+      while (!Args.empty()) {
+        const TemplateArgument &Argument = getArgument(Args.back());
+        if (!Argument.getIsDefaulted())
+          break;
+        Args = Args.drop_back();
+      }
+    }
+    // Drop trailing template arguments after any unexpanded pack.
+    for (size_t i = 0; i < Args.size(); ++i) {
+      const TemplateArgument &Argument = getArgument(Args[i]);
+      if (Argument.getKind() == TemplateArgument::Pack) {
+        Args = Args.take_front(i + 1);
+        break;
+      }
+    }
   }
 
   const char *Comma = Policy.MSVCFormatting ? "," : ", ";
diff --git a/clang/unittests/AST/DeclPrinterTest.cpp 
b/clang/unittests/AST/DeclPrinterTest.cpp
index 4368641850c20..a812315968f77 100644
--- a/clang/unittests/AST/DeclPrinterTest.cpp
+++ b/clang/unittests/AST/DeclPrinterTest.cpp
@@ -1588,3 +1588,31 @@ TEST(DeclPrinter, TestTemplateSuppressDeclAttributes) {
       "template <typename T> class A {}",
       [](PrintingPolicy &Policy) { Policy.SuppressDeclAttributes = true; }));
 }
+
+TEST(DeclPrinter, TestTemplateParameterPackDeduction) {
+  ASSERT_TRUE(PrintedDeclCXX17Matches(
+      "template<typename... Args>"
+      "void f(Args&&...);"
+      "void t() { f(short{}, double{}); }",
+      functionDecl(hasName("f"), isTemplateInstantiation()).bind("id"),
+      "template<> void f<short, double>(short &&, double &&)"));
+}
+
+TEST(DeclPrinter, TestTemplateParameterPackMultiDeduction) {
+  ASSERT_TRUE(PrintedDeclCXX17Matches(
+      "template<typename...> struct X {};"
+      "template<typename ...T, typename ...U> void f(X<T...>, X<U...>);"
+      "void g(X<int, char> x, X<float> y) { f(x, y); }",
+      functionDecl(hasName("f"), isTemplateInstantiation()).bind("id"),
+      "template<> void f<int, char>(X<int, char>, X<float>)"));
+}
+
+TEST(DeclPrinter, TestTemplateParameterPackForwarding) {
+  ASSERT_TRUE(PrintedDeclCXX17Matches(
+      "template<typename ...T> struct A {"
+      "  template<T ...A, typename ...B> void f(B...);"
+      "};"
+      "void g(A<int, int> a) { a.f<1, 2, int>(3); }",
+      functionDecl(hasName("f"), isTemplateInstantiation()).bind("id"),
+      "template<> void f<1, 2>(int)"));
+}

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

Reply via email to