aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrystian Stasiowski <sdkrystian@gmail.com>2024-06-18 13:40:31 -0400
committerGitHub <noreply@github.com>2024-06-18 13:40:31 -0400
commit9a88aa0e2b6d09c7c7932e14224632b2033ad403 (patch)
treef561bf984f0872e2fbfa5836d8590f60be576d45
parent35a2b60973074ab7b9add53c58acafe166820551 (diff)
downloadllvm-9a88aa0e2b6d09c7c7932e14224632b2033ad403.zip
llvm-9a88aa0e2b6d09c7c7932e14224632b2033ad403.tar.gz
llvm-9a88aa0e2b6d09c7c7932e14224632b2033ad403.tar.bz2
[Clang][Sema] Diagnose variable template explicit specializations with storage-class-specifiers (#93873)
According to [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`. Clang partially implements this, but a number of issues exist: 1. We don't diagnose class scope explicit specializations of variable templates with _storage-class-specifiers_, e.g. ``` struct A { template<typename T> static constexpr int x = 0; template<> static constexpr int x<void> = 1; // ill-formed, but clang accepts }; ```` 2. We incorrectly reject class scope explicit specializations of variable templates when `static` is not used, e.g. ``` struct A { template<typename T> static constexpr int x = 0; template<> constexpr int x<void> = 1; // error: non-static data member cannot be constexpr; did you intend to make it static? }; ```` 3. We don't diagnose dependent class scope explicit specializations of function templates with storage class specifiers, e.g. ``` template<typename T> struct A { template<typename U> static void f(); template<> static void f<int>(); // ill-formed, but clang accepts }; ```` This patch addresses these issues as follows: - # 1 is fixed by issuing a diagnostic when an explicit specialization of a variable template has storage class specifier - # 2 is fixed by considering any non-function declaration with any template parameter lists at class scope to be a static data member. This also allows for better error recovery (it's more likely the user intended to declare a variable template than a "field template"). - # 3 is fixed by checking whether a function template explicit specialization has a storage class specifier even when the primary template is not yet known. One thing to note is that it would be far simpler to diagnose this when parsing the _decl-specifier-seq_, but such an implementation would necessitate a refactor of `ParsedTemplateInfo` which I believe to be outside the scope of this patch.
-rw-r--r--clang/docs/ReleaseNotes.rst1
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--clang/lib/Parse/ParseDeclCXX.cpp5
-rw-r--r--clang/lib/Sema/DeclSpec.cpp1
-rw-r--r--clang/lib/Sema/SemaDecl.cpp248
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp28
-rw-r--r--clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp5
-rw-r--r--clang/test/CXX/drs/cwg7xx.cpp24
-rw-r--r--clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp2
-rw-r--r--clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp2
-rw-r--r--clang/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp15
-rw-r--r--clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp59
-rw-r--r--clang/test/CXX/temp/temp.spec/temp.expl.spec/p2-20.cpp274
-rw-r--r--clang/test/Modules/Inputs/redecl-templates/a.h2
-rw-r--r--clang/test/Modules/redecl-templates.cpp6
-rw-r--r--clang/test/PCH/cxx-templates.h12
-rw-r--r--clang/test/PCH/cxx1y-variable-templates.cpp24
-rw-r--r--clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp26
-rw-r--r--clang/test/SemaTemplate/explicit-specialization-member.cpp8
-rw-r--r--clang/test/SemaTemplate/nested-template.cpp14
20 files changed, 526 insertions, 233 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c88555d..bbbc043 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
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8f85371..1473678 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">;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 9a4a777..d02548f 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 60e8189..96c90a60 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 34a1654..e28e5c5 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 7718b24..d38700d 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 =
@@ -3559,28 +3559,6 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
}
IdentifierInfo *II = Name.getAsIdentifierInfo();
-
- // 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;
- }
-
if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
Diag(D.getIdentifierLoc(), diag::err_member_with_template_arguments)
<< II
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 cbb439e..f6b5d24 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 0300dae..6d93e29 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 feeb362..2a74429 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 3c500c2..16e668e 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 1b03962..904058e 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 c29646d..80fae70 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 0000000..7a261fe
--- /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}}
diff --git a/clang/test/Modules/Inputs/redecl-templates/a.h b/clang/test/Modules/Inputs/redecl-templates/a.h
index fd25fcf..205483f 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 ee42dc9..3ebafb5 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 95d684e..8927fd5 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 faa9b3d..9063b6e 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 2196bfb..5e189b7 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 5dc8118..c406fb3 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 5bd388d..a6ede8e 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() {