diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 778ce0e0e52d0..a3107c4a69532 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -518,7 +518,6 @@ Improvements to Clang's diagnostics - Clang now diagnoses definitions of friend function specializations, e.g. ``friend void f<>(int) {}``. - Clang now diagnoses narrowing conversions involving const references. (`#63151: `_). -- Clang now diagnoses unexpanded packs within the template argument lists of function template specializations. Improvements to Clang's time-trace diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8e46c4984d93d..2de631941325f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9900,15 +9900,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. bool Invalid = false; - TemplateIdAnnotation *TemplateId = - D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId - ? D.getName().TemplateId - : nullptr; TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), - D.getCXXScopeSpec(), TemplateId, TemplateParamLists, isFriend, - isMemberSpecialization, Invalid); + D.getCXXScopeSpec(), + D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId + ? D.getName().TemplateId + : nullptr, + TemplateParamLists, isFriend, isMemberSpecialization, + Invalid); if (TemplateParams) { // Check that we can declare a template here. if (CheckTemplateDeclScope(S, TemplateParams)) @@ -9921,11 +9921,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (Name.getNameKind() == DeclarationName::CXXDestructorName) { Diag(NewFD->getLocation(), diag::err_destructor_template); NewFD->setInvalidDecl(); - // Function template with explicit template arguments. - } else if (TemplateId) { - Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec) - << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc); - NewFD->setInvalidDecl(); } // If we're adding a template to a dependent context, we may need to @@ -9978,11 +9973,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, << FixItHint::CreateRemoval(RemoveRange) << FixItHint::CreateInsertion(InsertLoc, "<>"); Invalid = true; - - // Recover by faking up an empty template argument list. - HasExplicitTemplateArgs = true; - TemplateArgs.setLAngleLoc(InsertLoc); - TemplateArgs.setRAngleLoc(InsertLoc); } } } else { @@ -9996,33 +9986,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (TemplateParamLists.size() > 0) // For source fidelity, store all the template param lists. NewFD->setTemplateParameterListsInfo(Context, TemplateParamLists); - - // "friend void foo<>(int);" is an implicit specialization decl. - if (isFriend && TemplateId) - isFunctionTemplateSpecialization = true; - } - - // If this is a function template specialization and the unqualified-id of - // the declarator-id is a template-id, convert the template argument list - // into our AST format and check for unexpanded packs. - if (isFunctionTemplateSpecialization && TemplateId) { - HasExplicitTemplateArgs = true; - - TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc); - TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc); - ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), - TemplateId->NumArgs); - translateTemplateArguments(TemplateArgsPtr, TemplateArgs); - - // FIXME: Should we check for unexpanded packs if this was an (invalid) - // declaration of a function template partial specialization? Should we - // consider the unexpanded pack context to be a partial specialization? - for (const TemplateArgumentLoc &ArgLoc : TemplateArgs.arguments()) { - if (DiagnoseUnexpandedParameterPack( - ArgLoc, isFriend ? UPPC_FriendDeclaration - : UPPC_ExplicitSpecialization)) - NewFD->setInvalidDecl(); - } } if (Invalid) { @@ -10475,6 +10438,46 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, diag::ext_operator_new_delete_declared_inline) << NewFD->getDeclName(); + // If the declarator is a template-id, translate the parser's template + // argument list into our AST format. + if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) { + TemplateIdAnnotation *TemplateId = D.getName().TemplateId; + TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc); + TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc); + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + translateTemplateArguments(TemplateArgsPtr, + TemplateArgs); + + HasExplicitTemplateArgs = true; + + if (NewFD->isInvalidDecl()) { + HasExplicitTemplateArgs = false; + } else if (FunctionTemplate) { + // Function template with explicit template arguments. + Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec) + << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc); + + HasExplicitTemplateArgs = false; + } else if (isFriend) { + // "friend void foo<>(int);" is an implicit specialization decl. + isFunctionTemplateSpecialization = true; + } else { + assert(isFunctionTemplateSpecialization && + "should have a 'template<>' for this decl"); + } + } else if (isFriend && isFunctionTemplateSpecialization) { + // This combination is only possible in a recovery case; the user + // wrote something like: + // template <> friend void foo(int); + // which we're recovering from as if the user had written: + // friend void foo<>(int); + // Go ahead and fake up a template id. + HasExplicitTemplateArgs = true; + TemplateArgs.setLAngleLoc(D.getIdentifierLoc()); + TemplateArgs.setRAngleLoc(D.getIdentifierLoc()); + } + // We do not add HD attributes to specializations here because // they may have different constexpr-ness compared to their // templates and, after maybeAddCUDAHostDeviceAttrs() is applied, diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp index 3c500c2c4dc4a..30ce6b40e1fb5 100644 --- a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp @@ -376,11 +376,6 @@ namespace Specializations { template struct PrimaryClass; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}} - template - void PrimaryFunction(); - template - void PrimaryFunction(); // expected-error{{function template partial specialization is not allowed}} - #if __cplusplus >= 201402L template constexpr int PrimaryVar = 0; @@ -397,13 +392,6 @@ namespace Specializations { template struct InnerClass; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}} - template - void InnerFunction(); - template<> - void InnerFunction(); // expected-error{{explicit specialization contains unexpanded parameter pack 'Ts'}} - - friend void PrimaryFunction(); // expected-error{{friend declaration contains unexpanded parameter pack 'Ts'}} - #if __cplusplus >= 201402L template constexpr static int InnerVar = 0;