Skip to content

Commit 640ef55

Browse files
authored
Reapply "[Clang][Sema] Diagnose unexpanded packs in the template argument lists of function template specializations" (#76876) (#76915)
This reapplies f034044 after it was reverted by 687396b due to a test failure in clang-doc. The test in question declares a partial specialization of a function template, as well as an explicit specialization of the same function template. Both declarations are now set as invalid, meaning neither is emitted by clang-doc. Since this is the sole test of function template specializations in clang-doc, I presume the intent is for the partial specialization to actually be the primary template. Doing so results in the expected output.
1 parent 9215741 commit 640ef55

File tree

4 files changed

+57
-47
lines changed

4 files changed

+57
-47
lines changed

clang-tools-extra/test/clang-doc/templates.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// RUN: rm -rf %t
88

99
template<typename T, int U = 1>
10-
void function<bool, 0>(T x) {}
10+
void function(T x) {}
1111

1212
template<>
1313
void function<bool, 0>(bool x) {}

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ Improvements to Clang's diagnostics
518518
- Clang now diagnoses definitions of friend function specializations, e.g. ``friend void f<>(int) {}``.
519519
- Clang now diagnoses narrowing conversions involving const references.
520520
(`#63151: <https://github.com/llvm/llvm-project/issues/63151>`_).
521+
- Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
521522

522523

523524
Improvements to Clang's time-trace

clang/lib/Sema/SemaDecl.cpp

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9900,15 +9900,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
99009900
// Match up the template parameter lists with the scope specifier, then
99019901
// determine whether we have a template or a template specialization.
99029902
bool Invalid = false;
9903+
TemplateIdAnnotation *TemplateId =
9904+
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
9905+
? D.getName().TemplateId
9906+
: nullptr;
99039907
TemplateParameterList *TemplateParams =
99049908
MatchTemplateParametersToScopeSpecifier(
99059909
D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
9906-
D.getCXXScopeSpec(),
9907-
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
9908-
? D.getName().TemplateId
9909-
: nullptr,
9910-
TemplateParamLists, isFriend, isMemberSpecialization,
9911-
Invalid);
9910+
D.getCXXScopeSpec(), TemplateId, TemplateParamLists, isFriend,
9911+
isMemberSpecialization, Invalid);
99129912
if (TemplateParams) {
99139913
// Check that we can declare a template here.
99149914
if (CheckTemplateDeclScope(S, TemplateParams))
@@ -9921,6 +9921,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
99219921
if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
99229922
Diag(NewFD->getLocation(), diag::err_destructor_template);
99239923
NewFD->setInvalidDecl();
9924+
// Function template with explicit template arguments.
9925+
} else if (TemplateId) {
9926+
Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec)
9927+
<< SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc);
9928+
NewFD->setInvalidDecl();
99249929
}
99259930

99269931
// If we're adding a template to a dependent context, we may need to
@@ -9973,6 +9978,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
99739978
<< FixItHint::CreateRemoval(RemoveRange)
99749979
<< FixItHint::CreateInsertion(InsertLoc, "<>");
99759980
Invalid = true;
9981+
9982+
// Recover by faking up an empty template argument list.
9983+
HasExplicitTemplateArgs = true;
9984+
TemplateArgs.setLAngleLoc(InsertLoc);
9985+
TemplateArgs.setRAngleLoc(InsertLoc);
99769986
}
99779987
}
99789988
} else {
@@ -9986,6 +9996,33 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
99869996
if (TemplateParamLists.size() > 0)
99879997
// For source fidelity, store all the template param lists.
99889998
NewFD->setTemplateParameterListsInfo(Context, TemplateParamLists);
9999+
10000+
// "friend void foo<>(int);" is an implicit specialization decl.
10001+
if (isFriend && TemplateId)
10002+
isFunctionTemplateSpecialization = true;
10003+
}
10004+
10005+
// If this is a function template specialization and the unqualified-id of
10006+
// the declarator-id is a template-id, convert the template argument list
10007+
// into our AST format and check for unexpanded packs.
10008+
if (isFunctionTemplateSpecialization && TemplateId) {
10009+
HasExplicitTemplateArgs = true;
10010+
10011+
TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc);
10012+
TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc);
10013+
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
10014+
TemplateId->NumArgs);
10015+
translateTemplateArguments(TemplateArgsPtr, TemplateArgs);
10016+
10017+
// FIXME: Should we check for unexpanded packs if this was an (invalid)
10018+
// declaration of a function template partial specialization? Should we
10019+
// consider the unexpanded pack context to be a partial specialization?
10020+
for (const TemplateArgumentLoc &ArgLoc : TemplateArgs.arguments()) {
10021+
if (DiagnoseUnexpandedParameterPack(
10022+
ArgLoc, isFriend ? UPPC_FriendDeclaration
10023+
: UPPC_ExplicitSpecialization))
10024+
NewFD->setInvalidDecl();
10025+
}
998910026
}
999010027

999110028
if (Invalid) {
@@ -10438,46 +10475,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1043810475
diag::ext_operator_new_delete_declared_inline)
1043910476
<< NewFD->getDeclName();
1044010477

10441-
// If the declarator is a template-id, translate the parser's template
10442-
// argument list into our AST format.
10443-
if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
10444-
TemplateIdAnnotation *TemplateId = D.getName().TemplateId;
10445-
TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc);
10446-
TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc);
10447-
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
10448-
TemplateId->NumArgs);
10449-
translateTemplateArguments(TemplateArgsPtr,
10450-
TemplateArgs);
10451-
10452-
HasExplicitTemplateArgs = true;
10453-
10454-
if (NewFD->isInvalidDecl()) {
10455-
HasExplicitTemplateArgs = false;
10456-
} else if (FunctionTemplate) {
10457-
// Function template with explicit template arguments.
10458-
Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec)
10459-
<< SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc);
10460-
10461-
HasExplicitTemplateArgs = false;
10462-
} else if (isFriend) {
10463-
// "friend void foo<>(int);" is an implicit specialization decl.
10464-
isFunctionTemplateSpecialization = true;
10465-
} else {
10466-
assert(isFunctionTemplateSpecialization &&
10467-
"should have a 'template<>' for this decl");
10468-
}
10469-
} else if (isFriend && isFunctionTemplateSpecialization) {
10470-
// This combination is only possible in a recovery case; the user
10471-
// wrote something like:
10472-
// template <> friend void foo(int);
10473-
// which we're recovering from as if the user had written:
10474-
// friend void foo<>(int);
10475-
// Go ahead and fake up a template id.
10476-
HasExplicitTemplateArgs = true;
10477-
TemplateArgs.setLAngleLoc(D.getIdentifierLoc());
10478-
TemplateArgs.setRAngleLoc(D.getIdentifierLoc());
10479-
}
10480-
1048110478
// We do not add HD attributes to specializations here because
1048210479
// they may have different constexpr-ness compared to their
1048310480
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,

clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ namespace Specializations {
376376
template<typename... Ts>
377377
struct PrimaryClass<Ts>; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}}
378378

379+
template<typename T, typename... Ts>
380+
void PrimaryFunction();
381+
template<typename T, typename... Ts>
382+
void PrimaryFunction<Ts>(); // expected-error{{function template partial specialization is not allowed}}
383+
379384
#if __cplusplus >= 201402L
380385
template<typename T, typename... Ts>
381386
constexpr int PrimaryVar = 0;
@@ -392,6 +397,13 @@ namespace Specializations {
392397
template<typename U>
393398
struct InnerClass<U, Ts>; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}}
394399

400+
template<typename... Us>
401+
void InnerFunction();
402+
template<>
403+
void InnerFunction<Ts>(); // expected-error{{explicit specialization contains unexpanded parameter pack 'Ts'}}
404+
405+
friend void PrimaryFunction<Ts>(); // expected-error{{friend declaration contains unexpanded parameter pack 'Ts'}}
406+
395407
#if __cplusplus >= 201402L
396408
template<typename... Us>
397409
constexpr static int InnerVar = 0;

0 commit comments

Comments
 (0)