Skip to content

Commit 17f0680

Browse files
authored
[Clang][Sema] Abbreviated function templates do not append invented parameters to empty template parameter lists (#80864)
According to [dcl.fct] p23: > An abbreviated function template can have a _template-head_. The invented _template-parameters_ are appended to the _template-parameter-list_ after the explicitly declared _template-parameters_. `template<>` is not a _template-head_ -- a _template-head_ must have at least one _template-parameter_. This patch corrects our current behavior of appending the invented template parameters to the innermost template parameter list, regardless of whether it is empty. Example: ``` template<typename T> struct A { void f(auto); }; template<> void A<int>::f(auto); // ok template<> template<> // warning: extraneous template parameter list in template specialization void A<int>::f(auto); ```
1 parent abc4f74 commit 17f0680

File tree

8 files changed

+44
-5
lines changed

8 files changed

+44
-5
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ Bug Fixes to C++ Support
215215
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
216216
- Fix a crash when trying to call a varargs function that also has an explicit object parameter.
217217
Fixes (`#80971 ICE when explicit object parameter be a function parameter pack`)
218+
- Fixed a bug where abbreviated function templates would append their invented template parameters to
219+
an empty template parameter lists.
218220

219221
Bug Fixes to AST Handling
220222
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/DeclTemplate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class TemplateParameterList final
134134
const_iterator end() const { return begin() + NumParams; }
135135

136136
unsigned size() const { return NumParams; }
137+
bool empty() const { return NumParams == 0; }
137138

138139
ArrayRef<NamedDecl *> asArray() { return llvm::ArrayRef(begin(), end()); }
139140
ArrayRef<const NamedDecl*> asArray() const {

clang/lib/AST/DeclPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,10 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
12151215
bool OmitTemplateKW) {
12161216
assert(Params);
12171217

1218+
// Don't print invented template parameter lists.
1219+
if (!Params->empty() && Params->getParam(0)->isImplicit())
1220+
return;
1221+
12181222
if (!OmitTemplateKW)
12191223
Out << "template ";
12201224
Out << '<';

clang/lib/Sema/SemaDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9759,7 +9759,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
97599759
SmallVector<TemplateParameterList *, 4> TemplateParamLists;
97609760
llvm::append_range(TemplateParamLists, TemplateParamListsRef);
97619761
if (TemplateParameterList *Invented = D.getInventedTemplateParameterList()) {
9762-
if (!TemplateParamLists.empty() &&
9762+
if (!TemplateParamLists.empty() && !TemplateParamLists.back()->empty() &&
97639763
Invented->getDepth() == TemplateParamLists.back()->getDepth())
97649764
TemplateParamLists.back() = Invented;
97659765
else

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19294,7 +19294,16 @@ void Sema::ActOnStartFunctionDeclarationDeclarator(
1929419294
ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid,
1929519295
/*SuppressDiagnostic=*/true);
1929619296
}
19297-
if (ExplicitParams) {
19297+
// C++23 [dcl.fct]p23:
19298+
// An abbreviated function template can have a template-head. The invented
19299+
// template-parameters are appended to the template-parameter-list after
19300+
// the explicitly declared template-parameters.
19301+
//
19302+
// A template-head must have one or more template-parameters (read:
19303+
// 'template<>' is *not* a template-head). Only append the invented
19304+
// template parameters if we matched the nested-name-specifier to a non-empty
19305+
// TemplateParameterList.
19306+
if (ExplicitParams && !ExplicitParams->empty()) {
1929819307
Info.AutoTemplateParameterDepth = ExplicitParams->getDepth();
1929919308
llvm::append_range(Info.TemplateParams, *ExplicitParams);
1930019309
Info.NumExplicitTemplateParams = ExplicitParams->size();

clang/test/AST/ast-print-method-decl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ struct DelegatingCtor2 {
3232

3333
// CHECK: struct DelegatingCtor3 {
3434
struct DelegatingCtor3 {
35-
// FIXME: template <> should not be output
36-
// CHECK: template <> DelegatingCtor3(auto);
35+
// CHECK: DelegatingCtor3(auto);
3736
DelegatingCtor3(auto);
3837

3938
// FIXME: Implicitly specialized method should not be output
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -std=c++20 -pedantic-errors -verify %s
2+
3+
// FIXME: This should be an error with -pedantic-errors.
4+
template<> // expected-warning {{extraneous template parameter list in template specialization}}
5+
void f(auto);
6+
7+
template<typename>
8+
void f(auto);
9+
10+
template<typename T>
11+
struct A {
12+
void g(auto);
13+
};
14+
15+
template<typename T>
16+
void A<T>::g(auto) { }
17+
18+
template<>
19+
void A<int>::g(auto) { }
20+
21+
// FIXME: This should be an error with -pedantic-errors.
22+
template<>
23+
template<> // expected-warning {{extraneous template parameter list in template specialization}}
24+
void A<long>::g(auto) { }

clang/test/OpenMP/for_loop_auto.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#ifndef HEADER
1111
#define HEADER
1212

13-
// CHECK: template <> void do_loop(const auto &v) {
13+
// CHECK: void do_loop(const auto &v) {
1414
// CHECK-NEXT: #pragma omp parallel for
1515
// CHECK-NEXT: for (const auto &i : v)
1616
// CHECK-NEXT: ;

0 commit comments

Comments
 (0)