diff options
author | Krystian Stasiowski <sdkrystian@gmail.com> | 2024-06-18 13:40:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-18 13:40:31 -0400 |
commit | 9a88aa0e2b6d09c7c7932e14224632b2033ad403 (patch) | |
tree | f561bf984f0872e2fbfa5836d8590f60be576d45 | |
parent | 35a2b60973074ab7b9add53c58acafe166820551 (diff) | |
download | llvm-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.
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() { |