[clang] Revert "[Clang][Sema] Diagnose unexpanded packs in the template argument lists of function template specializations" (PR #76876)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 3 15:16:36 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

Reverts llvm/llvm-project#<!-- -->76677

See discussion here: https://github.com/llvm/llvm-project/pull/76677

---
Full diff: https://github.com/llvm/llvm-project/pull/76876.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (-1) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+46-43) 
- (modified) clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp (-12) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 778ce0e0e52d06..a3107c4a695321 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: <https://github.com/llvm/llvm-project/issues/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 8e46c4984d93dc..2de631941325fa 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 3c500c2c4dc4a7..30ce6b40e1fb5f 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<typename... Ts>
   struct PrimaryClass<Ts>; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}}
 
-  template<typename T, typename... Ts>
-  void PrimaryFunction();
-  template<typename T, typename... Ts>
-  void PrimaryFunction<Ts>(); // expected-error{{function template partial specialization is not allowed}}
-
 #if __cplusplus >= 201402L
   template<typename T, typename... Ts>
   constexpr int PrimaryVar = 0;
@@ -397,13 +392,6 @@ namespace Specializations {
     template<typename U>
     struct InnerClass<U, Ts>; // expected-error{{partial specialization contains unexpanded parameter pack 'Ts'}}
 
-    template<typename... Us>
-    void InnerFunction();
-    template<>
-    void InnerFunction<Ts>(); // expected-error{{explicit specialization contains unexpanded parameter pack 'Ts'}}
-
-    friend void PrimaryFunction<Ts>(); // expected-error{{friend declaration contains unexpanded parameter pack 'Ts'}}
-
 #if __cplusplus >= 201402L
     template<typename... Us>
     constexpr static int InnerVar = 0;

``````````

</details>


https://github.com/llvm/llvm-project/pull/76876


More information about the cfe-commits mailing list