https://github.com/sdkrystian updated 
https://github.com/llvm/llvm-project/pull/93873

>From 40b69a190a1af610d946108394a38e3ac93dbd71 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Tue, 28 May 2024 07:42:06 -0400
Subject: [PATCH 1/5] [Clang][Sema] Diagnose variable template explicit
 specializations with storage-class-specifiers

---
 clang/lib/Parse/ParseDeclCXX.cpp              |   5 +-
 clang/lib/Sema/DeclSpec.cpp                   |   1 +
 clang/lib/Sema/SemaDecl.cpp                   | 248 ++++++++--------
 clang/lib/Sema/SemaDeclCXX.cpp                |   8 +-
 .../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp  |   5 +-
 clang/test/CXX/drs/cwg7xx.cpp                 |  24 +-
 .../test/CXX/temp/temp.decls/temp.mem/p2.cpp  |   2 +-
 .../CXX/temp/temp.decls/temp.variadic/p5.cpp  |   2 +-
 .../CXX/temp/temp.spec/temp.expl.spec/p17.cpp |  15 +-
 .../temp/temp.spec/temp.expl.spec/p2-0x.cpp   |  59 ++--
 .../temp/temp.spec/temp.expl.spec/p2-20.cpp   | 274 ++++++++++++++++++
 .../test/Modules/Inputs/redecl-templates/a.h  |   2 +-
 clang/test/Modules/redecl-templates.cpp       |   6 +-
 clang/test/PCH/cxx-templates.h                |  12 +-
 clang/test/PCH/cxx1y-variable-templates.cpp   |  24 +-
 .../cxx1y-variable-templates_in_class.cpp     |  26 +-
 .../explicit-specialization-member.cpp        |   8 +-
 clang/test/SemaTemplate/nested-template.cpp   |  14 +-
 18 files changed, 527 insertions(+), 208 deletions(-)
 create mode 100644 clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 9a4a777f575b2..d02548f6441f9 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3162,7 +3162,8 @@ Parser::DeclGroupPtrTy 
Parser::ParseCXXClassMemberDeclaration(
                      DeclSpec::SCS_static &&
                  DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
                      DeclSpec::SCS_typedef &&
-                 !DS.isFriendSpecified()) {
+                 !DS.isFriendSpecified() &&
+                 TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate) {
         // It's a default member initializer.
         if (BitfieldSize.get())
           Diag(Tok, getLangOpts().CPlusPlus20
@@ -3261,7 +3262,7 @@ Parser::DeclGroupPtrTy 
Parser::ParseCXXClassMemberDeclaration(
       } else if (ThisDecl)
         Actions.AddInitializerToDecl(ThisDecl, Init.get(),
                                      EqualLoc.isInvalid());
-    } else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static)
+    } else if (ThisDecl && DeclaratorInfo.isStaticMember())
       // No initializer.
       Actions.ActOnUninitializedDecl(ThisDecl);
 
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 60e8189025700..96c90a60b9682 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -416,6 +416,7 @@ bool Declarator::isDeclarationOfFunction() const {
 bool Declarator::isStaticMember() {
   assert(getContext() == DeclaratorContext::Member);
   return getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static ||
+         (!isDeclarationOfFunction() && !getTemplateParameterLists().empty()) 
||
          (getName().getKind() == UnqualifiedIdKind::IK_OperatorFunctionId &&
           CXXMethodDecl::isStaticOverloadedOperator(
               getName().OperatorFunctionId.Operator));
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 34a1654be134f..e28e5c56c11a7 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7609,80 +7609,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
                             NTCUC_AutoVar, NTCUK_Destruct);
   } else {
     bool Invalid = false;
-
-    if (DC->isRecord() && !CurContext->isRecord()) {
-      // This is an out-of-line definition of a static data member.
-      switch (SC) {
-      case SC_None:
-        break;
-      case SC_Static:
-        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
-             diag::err_static_out_of_line)
-          << 
FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
-        break;
-      case SC_Auto:
-      case SC_Register:
-      case SC_Extern:
-        // [dcl.stc] p2: The auto or register specifiers shall be applied only
-        // to names of variables declared in a block or to function parameters.
-        // [dcl.stc] p6: The extern specifier cannot be used in the declaration
-        // of class members
-
-        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
-             diag::err_storage_class_for_static_member)
-          << 
FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
-        break;
-      case SC_PrivateExtern:
-        llvm_unreachable("C storage class in c++!");
-      }
-    }
-
-    if (SC == SC_Static && CurContext->isRecord()) {
-      if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
-        // Walk up the enclosing DeclContexts to check for any that are
-        // incompatible with static data members.
-        const DeclContext *FunctionOrMethod = nullptr;
-        const CXXRecordDecl *AnonStruct = nullptr;
-        for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) {
-          if (Ctxt->isFunctionOrMethod()) {
-            FunctionOrMethod = Ctxt;
-            break;
-          }
-          const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt);
-          if (ParentDecl && !ParentDecl->getDeclName()) {
-            AnonStruct = ParentDecl;
-            break;
-          }
-        }
-        if (FunctionOrMethod) {
-          // C++ [class.static.data]p5: A local class shall not have static 
data
-          // members.
-          Diag(D.getIdentifierLoc(),
-               diag::err_static_data_member_not_allowed_in_local_class)
-              << Name << RD->getDeclName()
-              << llvm::to_underlying(RD->getTagKind());
-        } else if (AnonStruct) {
-          // C++ [class.static.data]p4: Unnamed classes and classes contained
-          // directly or indirectly within unnamed classes shall not contain
-          // static data members.
-          Diag(D.getIdentifierLoc(),
-               diag::err_static_data_member_not_allowed_in_anon_struct)
-              << Name << llvm::to_underlying(AnonStruct->getTagKind());
-          Invalid = true;
-        } else if (RD->isUnion()) {
-          // C++98 [class.union]p1: If a union contains a static data member,
-          // the program is ill-formed. C++11 drops this restriction.
-          Diag(D.getIdentifierLoc(),
-               getLangOpts().CPlusPlus11
-                 ? diag::warn_cxx98_compat_static_data_member_in_union
-                 : diag::ext_static_data_member_in_union) << Name;
-        }
-      }
-    }
-
     // Match up the template parameter lists with the scope specifier, then
     // determine whether we have a template or a template specialization.
-    bool InvalidScope = false;
     TemplateParams = MatchTemplateParametersToScopeSpecifier(
         D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
         D.getCXXScopeSpec(),
@@ -7690,8 +7618,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
             ? D.getName().TemplateId
             : nullptr,
         TemplateParamLists,
-        /*never a friend*/ false, IsMemberSpecialization, InvalidScope);
-    Invalid |= InvalidScope;
+        /*never a friend*/ false, IsMemberSpecialization, Invalid);
 
     if (TemplateParams) {
       if (!TemplateParams->size() &&
@@ -7734,6 +7661,102 @@ NamedDecl *Sema::ActOnVariableDeclarator(
              "should have a 'template<>' for this decl");
     }
 
+    bool IsExplicitSpecialization =
+        IsVariableTemplateSpecialization && !IsPartialSpecialization;
+
+    // C++ [temp.expl.spec]p2:
+    //   The declaration in an explicit-specialization shall not be an
+    //   export-declaration. An explicit specialization shall not use a
+    //   storage-class-specifier other than thread_local.
+    //
+    // We use the storage-class-specifier from DeclSpec because we may have
+    // added implicit 'extern' for declarations with __declspec(dllimport)!
+    if (SCSpec != DeclSpec::SCS_unspecified &&
+        (IsExplicitSpecialization || IsMemberSpecialization)) {
+      Diag(D.getDeclSpec().getStorageClassSpecLoc(),
+           diag::ext_explicit_specialization_storage_class)
+          << 
FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
+    }
+
+    if (CurContext->isRecord()) {
+      if (SC == SC_Static) {
+        if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
+          // Walk up the enclosing DeclContexts to check for any that are
+          // incompatible with static data members.
+          const DeclContext *FunctionOrMethod = nullptr;
+          const CXXRecordDecl *AnonStruct = nullptr;
+          for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) {
+            if (Ctxt->isFunctionOrMethod()) {
+              FunctionOrMethod = Ctxt;
+              break;
+            }
+            const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt);
+            if (ParentDecl && !ParentDecl->getDeclName()) {
+              AnonStruct = ParentDecl;
+              break;
+            }
+          }
+          if (FunctionOrMethod) {
+            // C++ [class.static.data]p5: A local class shall not have static
+            // data members.
+            Diag(D.getIdentifierLoc(),
+                 diag::err_static_data_member_not_allowed_in_local_class)
+                << Name << RD->getDeclName()
+                << llvm::to_underlying(RD->getTagKind());
+          } else if (AnonStruct) {
+            // C++ [class.static.data]p4: Unnamed classes and classes contained
+            // directly or indirectly within unnamed classes shall not contain
+            // static data members.
+            Diag(D.getIdentifierLoc(),
+                 diag::err_static_data_member_not_allowed_in_anon_struct)
+                << Name << llvm::to_underlying(AnonStruct->getTagKind());
+            Invalid = true;
+          } else if (RD->isUnion()) {
+            // C++98 [class.union]p1: If a union contains a static data member,
+            // the program is ill-formed. C++11 drops this restriction.
+            Diag(D.getIdentifierLoc(),
+                 getLangOpts().CPlusPlus11
+                     ? diag::warn_cxx98_compat_static_data_member_in_union
+                     : diag::ext_static_data_member_in_union)
+                << Name;
+          }
+        }
+      } else if (IsVariableTemplate || IsPartialSpecialization) {
+        // There is no such thing as a member field template.
+        Diag(D.getIdentifierLoc(), diag::err_template_member)
+            << II << TemplateParams->getSourceRange();
+        // Recover by pretending this is a static data member template.
+        SC = SC_Static;
+      }
+    } else if (DC->isRecord()) {
+      // This is an out-of-line definition of a static data member.
+      switch (SC) {
+      case SC_None:
+        break;
+      case SC_Static:
+        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
+             diag::err_static_out_of_line)
+            << FixItHint::CreateRemoval(
+                   D.getDeclSpec().getStorageClassSpecLoc());
+        break;
+      case SC_Auto:
+      case SC_Register:
+      case SC_Extern:
+        // [dcl.stc] p2: The auto or register specifiers shall be applied only
+        // to names of variables declared in a block or to function parameters.
+        // [dcl.stc] p6: The extern specifier cannot be used in the declaration
+        // of class members
+
+        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
+             diag::err_storage_class_for_static_member)
+            << FixItHint::CreateRemoval(
+                   D.getDeclSpec().getStorageClassSpecLoc());
+        break;
+      case SC_PrivateExtern:
+        llvm_unreachable("C storage class in c++!");
+      }
+    }
+
     if (IsVariableTemplateSpecialization) {
       SourceLocation TemplateKWLoc =
           TemplateParamLists.size() > 0
@@ -7779,8 +7802,6 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     // the variable (matching the scope specifier), store them.
     // An explicit variable template specialization does not own any template
     // parameter lists.
-    bool IsExplicitSpecialization =
-        IsVariableTemplateSpecialization && !IsPartialSpecialization;
     unsigned VDTemplateParamLists =
         (TemplateParams && !IsExplicitSpecialization) ? 1 : 0;
     if (TemplateParamLists.size() > VDTemplateParamLists)
@@ -10210,25 +10231,45 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator 
&D, DeclContext *DC,
       NewFD->setImplicitlyInline(ImplicitInlineCXX20);
     }
 
-    if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) &&
-        !CurContext->isRecord()) {
-      // C++ [class.static]p1:
-      //   A data or function member of a class may be declared static
-      //   in a class definition, in which case it is a static member of
-      //   the class.
+    if (!isFriend && SC != SC_None) {
+      // C++ [temp.expl.spec]p2:
+      //   The declaration in an explicit-specialization shall not be an
+      //   export-declaration. An explicit specialization shall not use a
+      //   storage-class-specifier other than thread_local.
+      //
+      // We diagnose friend declarations with storage-class-specifiers
+      // elsewhere.
+      if (isFunctionTemplateSpecialization || isMemberSpecialization) {
+        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
+             diag::ext_explicit_specialization_storage_class)
+            << FixItHint::CreateRemoval(
+                   D.getDeclSpec().getStorageClassSpecLoc());
+      }
 
-      // Complain about the 'static' specifier if it's on an out-of-line
-      // member function definition.
+      if (SC == SC_Static && !CurContext->isRecord() && DC->isRecord()) {
+        assert(isa<CXXMethodDecl>(NewFD) &&
+               "Out-of-line member function should be a CXXMethodDecl");
+        // C++ [class.static]p1:
+        //   A data or function member of a class may be declared static
+        //   in a class definition, in which case it is a static member of
+        //   the class.
 
-      // MSVC permits the use of a 'static' storage specifier on an out-of-line
-      // member function template declaration and class member template
-      // declaration (MSVC versions before 2015), warn about this.
-      Diag(D.getDeclSpec().getStorageClassSpecLoc(),
-           ((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
-             cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) ||
-           (getLangOpts().MSVCCompat && NewFD->getDescribedFunctionTemplate()))
-           ? diag::ext_static_out_of_line : diag::err_static_out_of_line)
-        << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
+        // Complain about the 'static' specifier if it's on an out-of-line
+        // member function definition.
+
+        // MSVC permits the use of a 'static' storage specifier on an
+        // out-of-line member function template declaration and class member
+        // template declaration (MSVC versions before 2015), warn about this.
+        Diag(D.getDeclSpec().getStorageClassSpecLoc(),
+             ((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
+               cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) ||
+              (getLangOpts().MSVCCompat &&
+               NewFD->getDescribedFunctionTemplate()))
+                 ? diag::ext_static_out_of_line
+                 : diag::err_static_out_of_line)
+            << FixItHint::CreateRemoval(
+                   D.getDeclSpec().getStorageClassSpecLoc());
+      }
     }
 
     // C++11 [except.spec]p15:
@@ -10596,27 +10637,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, 
DeclContext *DC,
                                                 Previous))
           NewFD->setInvalidDecl();
       }
-
-      // C++ [dcl.stc]p1:
-      //   A storage-class-specifier shall not be specified in an explicit
-      //   specialization (14.7.3)
-      // FIXME: We should be checking this for dependent specializations.
-      FunctionTemplateSpecializationInfo *Info =
-          NewFD->getTemplateSpecializationInfo();
-      if (Info && SC != SC_None) {
-        if (SC != Info->getTemplate()->getTemplatedDecl()->getStorageClass())
-          Diag(NewFD->getLocation(),
-               diag::err_explicit_specialization_inconsistent_storage_class)
-            << SC
-            << FixItHint::CreateRemoval(
-                                      
D.getDeclSpec().getStorageClassSpecLoc());
-
-        else
-          Diag(NewFD->getLocation(),
-               diag::ext_explicit_specialization_storage_class)
-            << FixItHint::CreateRemoval(
-                                      
D.getDeclSpec().getStorageClassSpecLoc());
-      }
     } else if (isMemberSpecialization && isa<CXXMethodDecl>(NewFD)) {
       if (CheckMemberSpecialization(NewFD, Previous))
           NewFD->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 7718b24955b06..dc0350d12b0ad 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -3508,9 +3508,9 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier 
AS, Declarator &D,
     break;
   }
 
-  bool isInstField = ((DS.getStorageClassSpec() == DeclSpec::SCS_unspecified ||
-                       DS.getStorageClassSpec() == DeclSpec::SCS_mutable) &&
-                      !isFunc);
+  bool isInstField = (DS.getStorageClassSpec() == DeclSpec::SCS_unspecified ||
+                      DS.getStorageClassSpec() == DeclSpec::SCS_mutable) &&
+                     !isFunc && TemplateParameterLists.empty();
 
   if (DS.hasConstexprSpecifier() && isInstField) {
     SemaDiagnosticBuilder B =
@@ -3560,6 +3560,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier 
AS, Declarator &D,
 
     IdentifierInfo *II = Name.getAsIdentifierInfo();
 
+#if 0
     // Member field could not be with "template" keyword.
     // So TemplateParameterLists should be empty in this case.
     if (TemplateParameterLists.size()) {
@@ -3580,6 +3581,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier 
AS, Declarator &D,
       }
       return nullptr;
     }
+#endif
 
     if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
       Diag(D.getIdentifierLoc(), diag::err_member_with_template_arguments)
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp 
b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp
index cbb439ef5fecd..f6b5d2487e73d 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp
@@ -7,7 +7,7 @@ template<typename T> void f(T) {}
 template<typename T> static void g(T) {}
 
 
-template<> static void f<int>(int); // expected-error{{explicit specialization 
has extraneous, inconsistent storage class 'static'}}
+template<> static void f<int>(int); // expected-warning{{explicit 
specialization cannot have a storage class}}
 template static void f<float>(float); // expected-error{{explicit 
instantiation cannot have a storage class}}
 
 template<> void f<double>(double);
@@ -29,4 +29,5 @@ int X<T>::value = 17;
 
 template static int X<int>::value; // expected-error{{explicit instantiation 
cannot have a storage class}}
 
-template<> static int X<float>::value; // expected-error{{'static' can only be 
specified inside the class definition}}
+template<> static int X<float>::value; // expected-warning{{explicit 
specialization cannot have a storage class}}
+                                       // expected-error@-1{{'static' can only 
be specified inside the class definition}}
diff --git a/clang/test/CXX/drs/cwg7xx.cpp b/clang/test/CXX/drs/cwg7xx.cpp
index 0300dae08d6d3..6d93e2948dadb 100644
--- a/clang/test/CXX/drs/cwg7xx.cpp
+++ b/clang/test/CXX/drs/cwg7xx.cpp
@@ -80,7 +80,7 @@ namespace cwg727 { // cwg727: partial
 
     template<> struct C<int>;
     template<> void f<int>();
-    template<> static int N<int>;
+    template<> int N<int>;
 
     template<typename T> struct C<T*>;
     template<typename T> static int N<T*>;
@@ -91,7 +91,7 @@ namespace cwg727 { // cwg727: partial
       //   expected-note@#cwg727-C {{explicitly specialized declaration is 
here}}
       template<> void f<float>();
       // expected-error@-1 {{no function template matches function template 
specialization 'f'}}
-      template<> static int N<float>;
+      template<> int N<float>;
       // expected-error@-1 {{variable template specialization of 'N' not in 
class 'A' or an enclosing namespace}}
       //   expected-note@#cwg727-N {{explicitly specialized declaration is 
here}}
 
@@ -109,7 +109,7 @@ namespace cwg727 { // cwg727: partial
       template<> void A::f<double>();
       // expected-error@-1 {{o function template matches function template 
specialization 'f'}}
       // expected-error@-2 {{non-friend class member 'f' cannot have a 
qualified name}}
-      template<> static int A::N<double>;
+      template<> int A::N<double>;
       // expected-error@-1 {{non-friend class member 'N' cannot have a 
qualified name}}
       // expected-error@-2 {{variable template specialization of 'N' not in 
class 'A' or an enclosing namespace}}
       //   expected-note@#cwg727-N {{explicitly specialized declaration is 
here}}
@@ -166,7 +166,7 @@ namespace cwg727 { // cwg727: partial
 
     template<> struct C<int> {};
     template<> void f<int>() {}
-    template<> static const int N<int>;
+    template<> const int N<int>;
 
     template<typename T> struct C<T*> {};
     template<typename T> static const int N<T*>;
@@ -208,18 +208,18 @@ namespace cwg727 { // cwg727: partial
 #if __cplusplus >= 201402L
     template<int> struct B {
       template<int> static const int u = 1;
-      template<> static const int u<0> = 2; // #cwg727-u0
+      template<> const int u<0> = 2; // #cwg727-u0
 
       // Note that in C++17 onwards, these are implicitly inline, and so the
       // initializer of v<0> is not instantiated with the declaration. In
       // C++14, v<0> is a non-defining declaration and its initializer is
       // instantiated with the class.
       template<int> static constexpr int v = 1;
-      template<> static constexpr int v<0> = 2; // #cwg727-v0
+      template<> constexpr int v<0> = 2; // #cwg727-v0
 
       template<int> static const inline int w = 1;
       // cxx14-error@-1 {{inline variables are a C++17 extension}}
-      template<> static const inline int w<0> = 2;
+      template<> const inline int w<0> = 2;
       // cxx14-error@-1 {{inline variables are a C++17 extension}}
     };
 
@@ -267,8 +267,8 @@ namespace cwg727 { // cwg727: partial
 
     template<typename> static int v1;
     // cxx98-11-error@-1 {{variable templates are a C++14 extension}}
-    template<> static int v1<T>; // #cwg727-v1-T
-    template<> static int v1<U>;
+    template<> int v1<T>; // #cwg727-v1-T
+    template<> int v1<U>;
     // expected-error@-1 {{duplicate member 'v1'}}
     //   expected-note@#cwg727-Collision-int-int {{in instantiation of 
template class 'cwg727::Collision<int, int>' requested here}}
     //   expected-note@#cwg727-v1-T {{previous}}
@@ -276,9 +276,9 @@ namespace cwg727 { // cwg727: partial
     template<typename> static inline int v2;
     // cxx98-11-error@-1 {{variable templates are a C++14 extension}}
     // cxx98-14-error@-2 {{inline variables are a C++17 extension}}
-    template<> static inline int v2<T>; // #cwg727-v2-T
-    // cxx98-14-error@-1 {{inline variables are a C++17 extension}} 
-    template<> static inline int v2<U>;
+    template<> inline int v2<T>; // #cwg727-v2-T
+    // cxx98-14-error@-1 {{inline variables are a C++17 extension}}
+    template<> inline int v2<U>;
     // cxx98-14-error@-1 {{inline variables are a C++17 extension}}
     // expected-error@-2 {{duplicate member 'v2'}}
     //   expected-note@#cwg727-v2-T {{previous declaration is here}}
diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp 
b/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp
index feeb362e34b40..2a74429bd7290 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp
@@ -9,6 +9,6 @@ void fun() {
     template <typename> void baz() {}   // expected-error{{templates cannot be 
declared inside of a local class}}
     template <typename> void qux();     // expected-error{{templates cannot be 
declared inside of a local class}}
     template <typename> using corge = int; // expected-error{{templates cannot 
be declared inside of a local class}}
-    template <typename T> static T grault; // expected-error{{static data 
member}} expected-error{{templates cannot be declared inside of a local class}}
+    template <typename T> static T grault; // expected-error{{templates cannot 
be declared inside of a local class}}
   };
 }
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..16e668e971a21 100644
--- a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
@@ -408,7 +408,7 @@ namespace Specializations {
     template<typename... Us>
     constexpr static int InnerVar = 0;
     template<>
-    constexpr static int InnerVar<Ts> = 0; // expected-error{{explicit 
specialization contains unexpanded parameter pack 'Ts'}}
+    constexpr int InnerVar<Ts> = 0; // expected-error{{explicit specialization 
contains unexpanded parameter pack 'Ts'}}
     template<typename U>
     constexpr static int InnerVar<U, Ts> = 0; // expected-error{{partial 
specialization contains unexpanded parameter pack 'Ts'}}
 #endif
diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp 
b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp
index 1b039627a1d3a..904058e71a7b8 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp
@@ -1,12 +1,12 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
-template<class T1> 
+template<class T1>
 class A {
   template<class T2> class B {
     void mf();
   };
 };
 
-template<> template<> class A<int>::B<double>; 
+template<> template<> class A<int>::B<double>;
 template<> template<> void A<char>::B<char>::mf();
 
 template<> void A<char>::B<int>::mf(); // expected-error{{requires 
'template<>'}}
@@ -17,15 +17,15 @@ namespace test1 {
     static int bar;
   };
   typedef A<int> AA;
-  
-  template <> int AA::foo = 0; 
+
+  template <> int AA::foo = 0;
   int AA::bar = 1; // expected-error {{template specialization requires 
'template<>'}}
   int A<float>::bar = 2; // expected-error {{template specialization requires 
'template<>'}}
 
-  template <> class A<double> { 
+  template <> class A<double> {
   public:
     static int foo;
-    static int bar;    
+    static int bar;
   };
 
   typedef A<double> AB;
@@ -40,7 +40,8 @@ struct S {
   int j<int>; // expected-error  {{member 'j' cannot have template arguments}}
 
   static int k<12>; // expected-error {{template specialization requires 
'template<>'}} \
-                       expected-error{{no variable template matches 
specialization}}
+                       expected-error {{no variable template matches 
specialization}} \
+                       expected-warning {{explicit specialization cannot have 
a storage class}}
   void f<12>();     // expected-error {{template specialization requires 
'template<>'}} \
                     // expected-error {{no function template matches function 
template specialization 'f'}}
 };
diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp 
b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp
index c29646dd94559..80fae707de0c4 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp
@@ -24,7 +24,7 @@ namespace N0 {
   void test_f0(NonDefaultConstructible NDC) {
     f0(NDC);
   }
-  
+
   template<> void f0(int);
   template<> void f0(long);
 }
@@ -39,34 +39,34 @@ template<> void N0::f0(double) { }
 
 struct X1 {
   template<typename T> void f(T);
-  
+
   template<> void f(int); // OK (DR727)
 };
 
 //     -- class template
 namespace N0 {
-  
+
 template<typename T>
 struct X0 { // expected-note {{here}}
   static T member;
-  
+
   void f1(T t) {
     t = 17;
   }
-  
+
   struct Inner : public T { }; // expected-note 2{{here}}
-  
+
   template<typename U>
   struct InnerTemplate : public T { }; // expected-note 1{{explicitly 
specialized}} \
    // expected-error{{base specifier}}
-  
+
   template<typename U>
   void ft1(T t, U u);
 };
 
 }
 
-template<typename T> 
+template<typename T>
 template<typename U>
 void N0::X0<T>::ft1(T t, U u) {
   t = u;
@@ -85,35 +85,36 @@ namespace N0 {
   template<> struct X0<volatile void>;
 }
 
-template<> struct N0::X0<volatile void> { 
+template<> struct N0::X0<volatile void> {
   void f1(void *);
 };
 
 //     -- variable template [C++1y]
 namespace N0 {
 template<typename T> int v0; // expected-note 4{{explicitly specialized 
declaration is here}}
-template<> extern int v0<char[1]>;
-template<> extern int v0<char[2]>;
-template<> extern int v0<char[5]>;
-template<> extern int v0<char[6]>;
+template<> int v0<char[1]>; // expected-note {{previous definition is here}}
+template<> int v0<char[2]>;
+template<> int v0<char[5]>; // expected-note {{previous definition is here}}
+template<> int v0<char[6]>;
 }
 using N0::v0;
 
 template<typename T> int v1; // expected-note 4{{explicitly specialized 
declaration is here}}
-template<> extern int v1<char[3]>;
-template<> extern int v1<char[4]>;
-template<> extern int v1<char[7]>;
-template<> extern int v1<char[8]>;
+template<> int v1<char[3]>; // expected-note {{previous definition is here}}
+template<> int v1<char[4]>; // expected-note {{previous definition is here}}
+template<> int v1<char[7]>; // expected-note {{previous definition is here}}
+template<> int v1<char[8]>;
 
 template<> int N0::v0<int[1]>;
 template<> int v0<int[2]>;
 template<> int ::v1<int[3]>; // expected-warning {{extra qualification}}
 template<> int v1<int[4]>;
 
-template<> int N0::v0<char[1]>;
+template<> int N0::v0<char[1]>; // expected-error {{redefinition of 
'v0<char[1]>'}}
 template<> int v0<char[2]>;
 template<> int ::v1<char[3]>; // expected-warning {{extra qualification}}
-template<> int v1<char[4]>;
+                              // expected-error@-1 {{redefinition of 
'v1<char[3]>'}}
+template<> int v1<char[4]>; // expected-error {{redefinition of 'v1<char[4]>'}}
 
 namespace N1 {
 template<> int N0::v0<int[5]>; // expected-error {{not in a namespace 
enclosing 'N0'}}
@@ -122,8 +123,10 @@ template<> int ::v1<int[7]>; // expected-error {{must 
occur at global scope}}
 template<> int v1<int[8]>; // expected-error {{must occur at global scope}}
 
 template<> int N0::v0<char[5]>; // expected-error {{not in a namespace 
enclosing 'N0'}}
+                                // expected-error@-1 {{redefinition of 
'v0<char[5]>'}}
 template<> int v0<char[6]>; // expected-error {{not in a namespace enclosing 
'N0'}}
 template<> int ::v1<char[7]>; // expected-error {{must occur at global scope}}
+                              // expected-error@-1 {{redefinition of 
'v1<char[7]>'}}
 template<> int v1<char[8]>; // expected-error {{must occur at global scope}}
 }
 
@@ -147,13 +150,13 @@ void test_x0_cvvoid(N0::X0<const volatile void*> x0, 
const volatile void *cvp) {
 //     -- static data member of a class template
 namespace N0 {
   // This actually tests p15; the following is a declaration, not a definition.
-  template<> 
+  template<>
   NonDefaultConstructible X0<NonDefaultConstructible>::member;
-  
+
   template<> long X0<long>::member = 17;
 
   template<> float X0<float>::member;
-  
+
   template<> double X0<double>::member;
 }
 
@@ -171,7 +174,7 @@ namespace N1 {
 
 //    -- member class of a class template
 namespace N0 {
-  
+
   template<>
   struct X0<void*>::Inner { };
 
@@ -213,7 +216,7 @@ namespace N0 {
   template<>
   template<>
   struct X0<void*>::InnerTemplate<int> { };
-  
+
   template<> template<>
   struct X0<int>::InnerTemplate<int>; // expected-note{{forward declaration}}
 
@@ -245,7 +248,7 @@ namespace N0 {
   template<>
   template<>
   void X0<void*>::ft1(void*, const void*) { }
-  
+
   template<> template<>
   void X0<void*>::ft1(void *, int);
 
@@ -279,7 +282,7 @@ namespace has_inline_namespaces {
   inline namespace inner {
     template<class T> void f(T&);
 
-    template<class T> 
+    template<class T>
     struct X0 {
       struct MemberClass;
 
@@ -330,10 +333,10 @@ template<> struct 
has_inline_namespaces::X0<X4>::MemberClass { };
 
 template<> void has_inline_namespaces::X0<X4>::mem_func();
 
-template<> template<typename T> 
+template<> template<typename T>
 struct has_inline_namespaces::X0<X4>::MemberClassTemplate { };
 
-template<> template<typename T> 
+template<> template<typename T>
 void has_inline_namespaces::X0<X4>::mem_func_template(T&) { }
 
 template<> int has_inline_namespaces::X0<X4>::value = 13;
diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp 
b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp
new file mode 100644
index 0000000000000..884c119556265
--- /dev/null
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp
@@ -0,0 +1,274 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+
+template<typename T>
+int x;
+
+template<typename T>
+static int x<T*>;
+
+template<>
+static int x<int>; // expected-warning {{explicit specialization cannot have a 
storage class}}
+
+template<typename T>
+extern int y;
+
+template<typename T>
+static int y<T*>;
+
+template<>
+static int y<int>; // expected-warning {{explicit specialization cannot have a 
storage class}}
+
+template<typename T>
+void f();
+
+template<>
+static void f<int>(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+
+template<typename T>
+extern void g();
+
+template<>
+static void g<int>(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+
+struct A {
+  static int x;
+
+  static int y;
+
+  static void f();
+
+  static void g();
+};
+
+int A::x = 0;
+
+static int A::y = 0; // expected-error {{'static' can only be specified inside 
the class definition}}
+
+void A::f() { }
+
+static void A::g() { } // expected-error {{'static' can only be specified 
inside the class definition}}
+
+struct B {
+  template<typename T>
+  static int x;
+
+  template<typename T>
+  static int y;
+
+  template<typename T>
+  int z; // expected-error {{member 'z' declared as a template}}
+
+  template<typename T>
+  static int x<T*>;
+
+  template<typename T>
+  static int y<T*>;
+
+  template<typename T>
+  int x<T**>; // expected-error {{member 'x' declared as a template}}
+
+  template<>
+  int x<short>;
+
+  template<>
+  static int x<long>; // expected-warning {{explicit specialization cannot 
have a storage class}}
+
+  template<typename T>
+  static void f();
+
+  template<typename T>
+  static void g();
+
+  template<>
+  void f<short>();
+
+  template<>
+  static void f<long>(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+};
+
+template<typename T>
+int B::x = 0;
+
+template<typename T>
+static int B::y = 0; // expected-error {{'static' can only be specified inside 
the class definition}}
+
+template<typename T>
+int B::x<T*> = 0;
+
+template<typename T>
+static int B::y<T*> = 0; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<typename T>
+int B::x<T***>;
+
+template<typename T>
+static int B::y<T***>; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<>
+int B::x<unsigned>;
+
+template<>
+static int B::y<unsigned>; // expected-warning {{explicit specialization 
cannot have a storage class}}
+                           // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
+
+template<typename T>
+void B::f() { }
+
+template<typename T>
+static void B::g() { } // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<>
+void B::f<unsigned>();
+
+template<>
+static void B::g<unsigned>(); // expected-warning {{explicit specialization 
cannot have a storage class}}
+                              // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
+
+template<typename T>
+struct C {
+  static int x;
+
+  static int y;
+
+  static void f();
+
+  static void g();
+};
+
+template<typename T>
+int C<T>::x = 0;
+
+template<typename T>
+static int C<T>::y = 0; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<typename T>
+void C<T>::f() { }
+
+template<typename T>
+static void C<T>::g() { } // expected-warning {{'static' can only be specified 
inside the class definition}}
+
+template<>
+int C<int>::x = 0;
+
+template<>
+static int C<int>::y = 0; // expected-warning {{explicit specialization cannot 
have a storage class}}
+                          // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
+
+template<>
+void C<int>::f();
+
+template<>
+static void C<int>::g(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+                         // expected-error@-1 {{'static' can only be specified 
inside the class definition}}
+template<typename T>
+struct D {
+  template<typename U>
+  static int x;
+
+  template<typename U>
+  static int y;
+
+  template<typename U>
+  int z; // expected-error {{member 'z' declared as a template}}
+
+  template<typename U>
+  static int x<U*>;
+
+  template<typename U>
+  static int y<U*>;
+
+  template<typename U>
+  int x<U**>; // expected-error {{member 'x' declared as a template}}
+
+  template<>
+  int x<short>;
+
+  template<>
+  static int x<long>; // expected-warning {{explicit specialization cannot 
have a storage class}}
+
+  template<typename U>
+  static void f();
+
+  template<typename U>
+  static void g();
+
+  template<>
+  void f<short>();
+
+  template<>
+  static void f<long>(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+};
+
+template<typename T>
+template<typename U>
+int D<T>::x = 0;
+
+template<typename T>
+template<typename U>
+static int D<T>::y = 0; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<typename T>
+template<typename U>
+int D<T>::x<U*> = 0;
+
+template<typename T>
+template<typename U>
+static int D<T>::y<U*> = 0; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<typename T>
+template<typename U>
+int D<T>::x<U***>;
+
+template<typename T>
+template<typename U>
+static int D<T>::y<U***>; // expected-error {{'static' can only be specified 
inside the class definition}}
+
+template<>
+template<typename U>
+int D<int>::x;
+
+template<>
+template<typename U>
+static int D<int>::y; // expected-warning {{explicit specialization cannot 
have a storage class}}
+                      // expected-error@-1 {{'static' can only be specified 
inside the class definition}}
+template<>
+template<typename U>
+int D<int>::x<U****>;
+
+template<>
+template<typename U>
+static int D<int>::y<U****>; // expected-warning {{explicit specialization 
cannot have a storage class}}
+                             // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
+template<>
+template<>
+int D<int>::x<unsigned>;
+
+template<>
+template<>
+static int D<int>::y<unsigned>; // expected-warning {{explicit specialization 
cannot have a storage class}}
+                                // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
+
+template<typename T>
+template<typename U>
+void D<T>::f() { }
+
+template<typename T>
+template<typename U>
+static void D<T>::g() { } // expected-warning {{'static' can only be specified 
inside the class definition}}
+
+template<>
+template<typename U>
+void D<int>::f();
+
+template<>
+template<typename U>
+static void D<int>::g(); // expected-warning {{explicit specialization cannot 
have a storage class}}
+                         // expected-error@-1 {{'static' can only be specified 
inside the class definition}}
+template<>
+template<>
+void D<int>::f<unsigned>();
+
+template<>
+template<>
+static void D<int>::g<unsigned>(); // expected-warning {{explicit 
specialization cannot have a storage class}}
+                                   // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
\ No newline at end of file
diff --git a/clang/test/Modules/Inputs/redecl-templates/a.h 
b/clang/test/Modules/Inputs/redecl-templates/a.h
index fd25fcf0768d2..205483fc01f12 100644
--- a/clang/test/Modules/Inputs/redecl-templates/a.h
+++ b/clang/test/Modules/Inputs/redecl-templates/a.h
@@ -5,4 +5,4 @@ template<int N> constexpr void f();
 template<> constexpr void f<1>();
 
 template<int N> extern int v;
-template<> extern int v<1>;
+template<> int v<1>;
diff --git a/clang/test/Modules/redecl-templates.cpp 
b/clang/test/Modules/redecl-templates.cpp
index ee42dc9c6a84c..3ebafb53dc3bc 100644
--- a/clang/test/Modules/redecl-templates.cpp
+++ b/clang/test/Modules/redecl-templates.cpp
@@ -1,7 +1,6 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 -x c++ -I %S/Inputs/redecl-templates %s -verify -std=c++14
 // RUN: %clang_cc1 -x c++ -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t -I %S/Inputs/redecl-templates %s -verify -std=c++14
-// expected-no-diagnostics
 
 template<int N> struct A {};
 template<int N> using X = A<N>;
@@ -29,4 +28,7 @@ int &x = w<1>;
 // instantiation of this specialization.
 template<> struct A<1> {};
 template<> constexpr void f<1>() {}
-template<> int v<1>;
+// Variable template explicit specializations are always definitions unless 
they
+// are static data members declared without an initializer.
+template<> int v<1>; // expected-error {{redefinition of 'v<1>'}}
+                     // expected-note@Inputs/redecl-templates/a.h:8 {{previous 
definition is here}}
diff --git a/clang/test/PCH/cxx-templates.h b/clang/test/PCH/cxx-templates.h
index 95d684e4a92db..8927fd55dcb83 100644
--- a/clang/test/PCH/cxx-templates.h
+++ b/clang/test/PCH/cxx-templates.h
@@ -49,7 +49,7 @@ struct Dep {
     int y = T::template my_templf<int>(0);
     ovl(y);
   }
-  
+
   void ovl(int);
   void ovl(float);
 };
@@ -67,7 +67,7 @@ template <class T> class UseBase {
 
 template <class T> class UseA : public UseBase<T> {
   using UseBase<T>::foo;
-  using typename UseBase<T>::bar; 
+  using typename UseBase<T>::bar;
 };
 
 template <class T> class Sub : public UseBase<int> { };
@@ -95,7 +95,7 @@ template<> bool isInt<8>(int x) {
 template<typename _CharT>
 int __copy_streambufs_eof(_CharT);
 
-class basic_streambuf 
+class basic_streambuf
 {
   void m() { }
   friend int __copy_streambufs_eof<>(int);
@@ -174,7 +174,7 @@ struct S7<int[N]> : S6<const int[N]> { };
 namespace ZeroLengthExplicitTemplateArgs {
   template<typename T> void h();
 
-  struct Y { 
+  struct Y {
     template<typename T> void f();
   };
 
@@ -417,11 +417,11 @@ namespace ClassScopeExplicitSpecializations {
   template<int> struct B {
     template<typename> static const int v = 1;
     template<typename T> static const int v<T*> = 2;
-    template<> static const int v<int> = 3;
+    template<> const int v<int> = 3;
 
     template<typename> static constexpr int w = 1;
     template<typename T> static constexpr int w<T*> = 2;
-    template<> static constexpr int w<int> = 3;
+    template<> constexpr int w<int> = 3;
   };
 
   template<> template<typename> constexpr int B<0>::v = 4;
diff --git a/clang/test/PCH/cxx1y-variable-templates.cpp 
b/clang/test/PCH/cxx1y-variable-templates.cpp
index faa9b3df22c1c..9063b6ee86938 100644
--- a/clang/test/PCH/cxx1y-variable-templates.cpp
+++ b/clang/test/PCH/cxx1y-variable-templates.cpp
@@ -67,11 +67,15 @@ namespace spec {
 
 namespace spec_join1 {
   template<typename T> T va = T(10);
-  template<> extern float va<float>;
+#ifdef ERROR
+  template<> float va<float>; // expected-note {{previous definition is here}}
+#endif
   extern template int va<int>;
 
   template<typename T> T vb = T(10);
-  template<> extern float vb<float>;
+#ifdef ERROR
+  template<> float vb<float>; // expected-note {{previous definition is here}}
+#endif
 
   template<typename T> T vc = T(10);
 
@@ -102,15 +106,19 @@ namespace join {
 
 namespace spec_join1 {
   template<typename T> extern T va;
-  template<> float va<float> = 1.5;
+#ifdef ERROR
+  template<> float va<float> = 1.5; // expected-error {{redefinition of 
'va<float>'}}
+#endif
   extern template int va<int>;
-  
-  template<> float vb<float> = 1.5;
+
+#ifdef ERROR
+  template<> float vb<float> = 1.5; // expected-error {{redefinition of 
'vb<float>'}}
+#endif
   template int vb<int>;
 
   template<> float vc<float> = 1.5;
   template int vc<int>;
-  
+
   template<typename T> extern T vd;
   template<typename T> T* vd<T*> = new T();
 }
@@ -123,9 +131,9 @@ namespace spec_join1 {
 template int var0a<int>;
 float fvara = var0a<float>;
 
-template<typename T> extern T var0a; 
+template<typename T> extern T var0a;
 
-template<typename T> T var0b = T(); 
+template<typename T> T var0b = T();
 template int var0b<int>;
 float fvarb = var0b<float>;
 
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp 
b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
index 2196bfb6eaac3..5e189b758c61e 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
@@ -15,8 +15,14 @@ class A {
   template<typename T> static CONST T right<T,int> = 5;
   template<typename T> CONST int right<int,T>;  // expected-error {{member 
'right' declared as a template}}
   template<typename T> CONST float right<float,T> = 5;  // expected-error 
{{member 'right' declared as a template}}
-  template<> static CONST int right<int,int> = 7;
-  template<> static CONST float right<float,int>;
+#ifdef PRECXX11
+                                                        // expected-warning@-2 
{{in-class initializer for static data member of type 'const float' is a GNU 
extension}}
+#else
+                                                        // expected-error@-4 
{{in-class initializer for static data member of type 'const float' requires 
'constexpr' specifier}}
+                                                        // expected-note@-5 
{{add 'constexpr'}}
+#endif
+  template<> CONST int right<int,int> = 7;
+  template<> CONST float right<float,int>;
   template static CONST int right<int,int>;     // expected-error {{expected 
'<' after 'template'}}
 };
 
@@ -155,16 +161,16 @@ namespace non_const_init {
 #ifndef PRECXX11
 namespace constexpred {
   class A {
-    template<typename T> constexpr T wrong;           // expected-error 
{{member 'wrong' declared as a template}} \
-                                                      // expected-error 
{{non-static data member cannot be constexpr; did you intend to make it const?}}
-    template<typename T> constexpr T wrong_init = 5;      // expected-error 
{{non-static data member cannot be constexpr; did you intend to make it 
static?}}
+    template<typename T> constexpr T wrong;           // expected-error 
{{member 'wrong' declared as a template}}
+                                                      // expected-error@-1 
{{declaration of constexpr static data member 'wrong' requires an initializer}}
+    template<typename T> constexpr T wrong_init = 5;  // expected-error 
{{member 'wrong_init' declared as a template}}
     template<typename T, typename T0> static constexpr T right = T(100);
     template<typename T> static constexpr T right<T,int> = 5;
-    template<typename T> constexpr int right<int,T>;  // expected-error 
{{member 'right' declared as a template}} \
-                                                      // expected-error 
{{non-static data member cannot be constexpr; did you intend to make it const?}}
-    template<typename T> constexpr float right<float,T> = 5;  // 
expected-error {{non-static data member cannot be constexpr; did you intend to 
make it static?}}
-    template<> static constexpr int right<int,int> = 7;
-    template <> static constexpr float right<float, int>; // expected-error 
{{declaration of constexpr static data member 'right<float, int>' requires an 
initializer}}
+    template<typename T> constexpr int right<int,T>;         // expected-error 
{{member 'right' declared as a template}}
+                                                             // 
expected-error@-1 {{declaration of constexpr static data member 'right<int, T>' 
requires an initializer}}
+    template<typename T> constexpr float right<float,T> = 5; // expected-error 
{{member 'right' declared as a template}}
+    template<> constexpr int right<int,int> = 7;
+    template<> constexpr float right<float, int>; // expected-error 
{{declaration of constexpr static data member 'right<float, int>' requires an 
initializer}}
     template static constexpr int right<int,int>;     // expected-error 
{{expected '<' after 'template'}}
   };
 }
diff --git a/clang/test/SemaTemplate/explicit-specialization-member.cpp 
b/clang/test/SemaTemplate/explicit-specialization-member.cpp
index 5dc8118556d42..c406fb3e21d40 100644
--- a/clang/test/SemaTemplate/explicit-specialization-member.cpp
+++ b/clang/test/SemaTemplate/explicit-specialization-member.cpp
@@ -2,7 +2,7 @@
 template<typename T>
 struct X0 {
   typedef T* type;
-  
+
   void f0(T);
   void f1(type);
 };
@@ -71,13 +71,13 @@ namespace PR41607 {
     };
 
     template<typename...> static int a;
-    template<> static constexpr int a<> = N;
+    template<> constexpr int a<> = N;
 
     template<typename...> static inline int b;
-    template<> static inline constexpr int b<> = N;
+    template<> inline constexpr int b<> = N;
 
     template<typename...> static constexpr int f();
-    template<> static constexpr int f() {
+    template<> constexpr int f() {
       return N;
     }
   };
diff --git a/clang/test/SemaTemplate/nested-template.cpp 
b/clang/test/SemaTemplate/nested-template.cpp
index 5bd388d4dff3d..a6ede8e99037e 100644
--- a/clang/test/SemaTemplate/nested-template.cpp
+++ b/clang/test/SemaTemplate/nested-template.cpp
@@ -3,7 +3,7 @@ class A;
 
 class S {
 public:
-   template<typename T> struct A { 
+   template<typename T> struct A {
      struct Nested {
        typedef T type;
      };
@@ -17,15 +17,15 @@ template<typename T>
 struct Outer {
   template<typename U>
   class Inner0;
-  
+
   template<typename U>
   class Inner1 {
     struct ReallyInner;
-    
+
     T foo(U);
     template<typename V> T bar(V);
     template<typename V> T* bar(V);
-    
+
     static T value1;
     static U value2;
   };
@@ -47,7 +47,7 @@ template<typename X>
 template<typename Y>
 struct Outer<X>::Inner1<Y>::ReallyInner {
   static Y value3;
-  
+
   void g(X, Y);
 };
 
@@ -130,10 +130,10 @@ namespace PR10896 {
   public:
     void foo() {}
   private:
-       
+
     template<typename T>
     T SomeField; // expected-error {{member 'SomeField' declared as a 
template}}
-    template<> int SomeField2; // expected-error {{extraneous 'template<>' in 
declaration of member 'SomeField2'}}
+    template<> int SomeField2; // expected-error {{extraneous 'template<>' in 
declaration of variable 'SomeField2'}}
   };
 
   void g() {

>From 205b36fd2288e2dc70a6c069e19d1a04d690e592 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Thu, 30 May 2024 17:05:38 -0400
Subject: [PATCH 2/5] [FOLD] cleanup

---
 clang/lib/Sema/SemaDeclCXX.cpp | 24 ------------------------
 1 file changed, 24 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index dc0350d12b0ad..d38700d56e4ff 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -3559,30 +3559,6 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier 
AS, Declarator &D,
     }
 
     IdentifierInfo *II = Name.getAsIdentifierInfo();
-
-#if 0
-    // Member field could not be with "template" keyword.
-    // So TemplateParameterLists should be empty in this case.
-    if (TemplateParameterLists.size()) {
-      TemplateParameterList* TemplateParams = TemplateParameterLists[0];
-      if (TemplateParams->size()) {
-        // There is no such thing as a member field template.
-        Diag(D.getIdentifierLoc(), diag::err_template_member)
-            << II
-            << SourceRange(TemplateParams->getTemplateLoc(),
-                TemplateParams->getRAngleLoc());
-      } else {
-        // There is an extraneous 'template<>' for this member.
-        Diag(TemplateParams->getTemplateLoc(),
-            diag::err_template_member_noparams)
-            << II
-            << SourceRange(TemplateParams->getTemplateLoc(),
-                TemplateParams->getRAngleLoc());
-      }
-      return nullptr;
-    }
-#endif
-
     if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
       Diag(D.getIdentifierLoc(), diag::err_member_with_template_arguments)
           << II

>From 6a0b20ccd49c7f9c3a2a81d87e4a0b121e083d77 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Thu, 30 May 2024 17:51:17 -0400
Subject: [PATCH 3/5] [FOLD] add newline

---
 clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp 
b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp
index 884c119556265..7a261fef27361 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp
@@ -271,4 +271,4 @@ void D<int>::f<unsigned>();
 template<>
 template<>
 static void D<int>::g<unsigned>(); // expected-warning {{explicit 
specialization cannot have a storage class}}
-                                   // expected-error@-1 {{'static' can only be 
specified inside the class definition}}
\ No newline at end of file
+                                   // expected-error@-1 {{'static' can only be 
specified inside the class definition}}

>From 4f5cc8596c6bfe1711bebaabb3164f726339a008 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Tue, 18 Jun 2024 09:52:58 -0400
Subject: [PATCH 4/5] [FOLD] add release note

---
 clang/docs/ReleaseNotes.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c88555dff8b28..bbbc04308df33 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -865,6 +865,7 @@ Bug Fixes to C++ Support
 - Fixed a failed assertion when attempting to convert an integer representing 
the difference
   between the addresses of two labels (a GNU extension) to a pointer within a 
constant expression. (#GH95366).
 - Fix immediate escalation bugs in the presence of dependent call arguments. 
(#GH94935)
+- Clang now diagnoses explicit specializations with storage class specifiers 
in all contexts.
 
 
 Bug Fixes to AST Handling

>From bfb535250128623fa2905c3d8e6064eda105f4c3 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Tue, 18 Jun 2024 13:12:58 -0400
Subject: [PATCH 5/5] [FOLD] remove unused diagnostic

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8f85371df3b85..14736784cff5f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5360,9 +5360,6 @@ def err_not_class_template_specialization : Error<
   "parameter}0">;
 def ext_explicit_specialization_storage_class : ExtWarn<
   "explicit specialization cannot have a storage class">;
-def err_explicit_specialization_inconsistent_storage_class : Error<
-  "explicit specialization has extraneous, inconsistent storage class "
-  "'%select{none|extern|static|__private_extern__|auto|register}0'">;
 def err_dependent_function_template_spec_no_match : Error<
   "no candidate function template was found for dependent"
   " %select{member|friend}0 function template specialization">;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to