https://github.com/kimgr updated https://github.com/llvm/llvm-project/pull/216570
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 1/2] 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)")); +} From 7a3cc19438e3c3dfb51c0ebf67fd52592fbbb873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Gr=C3=A4sman?= <[email protected]> Date: Sun, 16 Aug 2026 16:44:30 +0200 Subject: [PATCH 2/2] Fix up existing test regressions Not so sure about these changes, but they make sense given the new "drop all args after packs" policy. --- clang-tools-extra/clangd/unittests/FindTargetTests.cpp | 2 +- clang-tools-extra/clangd/unittests/HoverTests.cpp | 2 +- clang/test/AST/ast-dump-templates.cpp | 2 +- clang/test/SemaCXX/cxx2c-pack-indexing.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index d8c903cfcafeb..338f51a862829 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -798,7 +798,7 @@ TEST_F(TargetDeclTest, BuiltinTemplates) { EXPECT_DECLS( "TemplateSpecializationTypeLoc", {"struct integer_sequence", Rel::TemplatePattern | Rel::Underlying}, - {"template<> struct integer_sequence<int, <0, 1, 2>>", + {"template<> struct integer_sequence<int, 0, 1, 2>", Rel::TemplateInstantiation | Rel::Underlying}); // Dependent context. diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index d355118f30761..cb3a415698a17 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -410,7 +410,7 @@ class Foo final {})cpp"; HI.NamespaceScope = ""; HI.Name = "foo"; HI.Kind = index::SymbolKind::Function; - HI.Definition = "template <> void foo<Foo, char, 0, false, <>>()"; + HI.Definition = "template <> void foo<Foo, char, 0, false>()"; HI.ReturnType = "void"; HI.Type = "void ()"; HI.Parameters.emplace(); diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp index 22b4e8fb1dec7..7ccacf46052f5 100644 --- a/clang/test/AST/ast-dump-templates.cpp +++ b/clang/test/AST/ast-dump-templates.cpp @@ -36,7 +36,7 @@ void baz() { // Template instantiation - foo // Since the order of instantiation may vary during runs, run FileCheck twice // to make sure each instantiation is in the correct spot. -// CHECK1: template<> struct foo<5, int, 5> { +// CHECK1: template<> struct foo<5, int> { // CHECK2: template<> struct foo<2, double, 3> { // Template definition - bar diff --git a/clang/test/SemaCXX/cxx2c-pack-indexing.cpp b/clang/test/SemaCXX/cxx2c-pack-indexing.cpp index 0892394955a60..e9eeb962703e1 100644 --- a/clang/test/SemaCXX/cxx2c-pack-indexing.cpp +++ b/clang/test/SemaCXX/cxx2c-pack-indexing.cpp @@ -374,7 +374,7 @@ void foo(auto...x){ // expected-error@-2 2{{evaluates to 18446744073709551616, which cannot be narrowed to type '__size_t' (aka 'unsigned long')}} } int test(){ - (void)foo<int>(0); // expected-note {{in instantiation of function template specialization 'GH205650::foo<int, int>' requested here}} + (void)foo<int>(0); // expected-note {{in instantiation of function template specialization 'GH205650::foo<int>' requested here}} } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
