Skip to content

Commit

Permalink
[Clang][Sema] Abbreviated function templates do not append invented p…
Browse files Browse the repository at this point in the history
…arameters 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);
```
  • Loading branch information
sdkrystian committed Feb 8, 2024
1 parent abc4f74 commit 17f0680
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 5 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ Bug Fixes to C++ Support
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
- Fix a crash when trying to call a varargs function that also has an explicit object parameter.
Fixes (`#80971 ICE when explicit object parameter be a function parameter pack`)
- Fixed a bug where abbreviated function templates would append their invented template parameters to
an empty template parameter lists.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class TemplateParameterList final
const_iterator end() const { return begin() + NumParams; }

unsigned size() const { return NumParams; }
bool empty() const { return NumParams == 0; }

ArrayRef<NamedDecl *> asArray() { return llvm::ArrayRef(begin(), end()); }
ArrayRef<const NamedDecl*> asArray() const {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/DeclPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,10 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW) {
assert(Params);

// Don't print invented template parameter lists.
if (!Params->empty() && Params->getParam(0)->isImplicit())
return;

if (!OmitTemplateKW)
Out << "template ";
Out << '<';
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9759,7 +9759,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
SmallVector<TemplateParameterList *, 4> TemplateParamLists;
llvm::append_range(TemplateParamLists, TemplateParamListsRef);
if (TemplateParameterList *Invented = D.getInventedTemplateParameterList()) {
if (!TemplateParamLists.empty() &&
if (!TemplateParamLists.empty() && !TemplateParamLists.back()->empty() &&
Invented->getDepth() == TemplateParamLists.back()->getDepth())
TemplateParamLists.back() = Invented;
else
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19294,7 +19294,16 @@ void Sema::ActOnStartFunctionDeclarationDeclarator(
ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid,
/*SuppressDiagnostic=*/true);
}
if (ExplicitParams) {
// C++23 [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.
//
// A template-head must have one or more template-parameters (read:
// 'template<>' is *not* a template-head). Only append the invented
// template parameters if we matched the nested-name-specifier to a non-empty
// TemplateParameterList.
if (ExplicitParams && !ExplicitParams->empty()) {
Info.AutoTemplateParameterDepth = ExplicitParams->getDepth();
llvm::append_range(Info.TemplateParams, *ExplicitParams);
Info.NumExplicitTemplateParams = ExplicitParams->size();
Expand Down
3 changes: 1 addition & 2 deletions clang/test/AST/ast-print-method-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ struct DelegatingCtor2 {

// CHECK: struct DelegatingCtor3 {
struct DelegatingCtor3 {
// FIXME: template <> should not be output
// CHECK: template <> DelegatingCtor3(auto);
// CHECK: DelegatingCtor3(auto);
DelegatingCtor3(auto);

// FIXME: Implicitly specialized method should not be output
Expand Down
24 changes: 24 additions & 0 deletions clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p23.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %clang_cc1 -std=c++20 -pedantic-errors -verify %s

// FIXME: This should be an error with -pedantic-errors.
template<> // expected-warning {{extraneous template parameter list in template specialization}}
void f(auto);

template<typename>
void f(auto);

template<typename T>
struct A {
void g(auto);
};

template<typename T>
void A<T>::g(auto) { }

template<>
void A<int>::g(auto) { }

// FIXME: This should be an error with -pedantic-errors.
template<>
template<> // expected-warning {{extraneous template parameter list in template specialization}}
void A<long>::g(auto) { }
2 changes: 1 addition & 1 deletion clang/test/OpenMP/for_loop_auto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#ifndef HEADER
#define HEADER

// CHECK: template <> void do_loop(const auto &v) {
// CHECK: void do_loop(const auto &v) {
// CHECK-NEXT: #pragma omp parallel for
// CHECK-NEXT: for (const auto &i : v)
// CHECK-NEXT: ;
Expand Down

0 comments on commit 17f0680

Please sign in to comment.