From: Douglas Gregor Date: Tue, 10 May 2011 18:27:06 +0000 (+0000) Subject: Reimplement Sema::MatchTemplateParametersToScopeSpecifier() based on X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c840649ed5acccf68e1bc5a9d22f2ea017074586;p=clang Reimplement Sema::MatchTemplateParametersToScopeSpecifier() based on the semantic context referenced by the nested-name-specifier rather than the syntactic form of the nested-name-specifier. The previous incarnation was based on my complete misunderstanding of C++ [temp.expl.spec]. The latest C++0x working draft clarifies the requirements here, and this rewrite is intended to follow that. Along the way, improve source location information in the diagnostics. For example, if we report that a specific type needs or doesn't need a 'template<>' header, we dig out that type in the nested-name-specifier and highlight its range. Fixes: PR5907, PR9421, PR8277, PR8708, PR9482, PR9668, PR9877, and . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131138 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 95ff9e8695..60b327251a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3491,6 +3491,7 @@ public: TemplateParamListContext TPC); TemplateParameterList * MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, + SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateParameterList **ParamLists, unsigned NumParamLists, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e987cdaf7e..e19b06effe 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3557,7 +3557,8 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, bool Invalid = false; if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( - D.getDeclSpec().getSourceRange().getBegin(), + D.getDeclSpec().getSourceRange().getBegin(), + D.getIdentifierLoc(), D.getCXXScopeSpec(), TemplateParamLists.get(), TemplateParamLists.size(), @@ -3579,7 +3580,6 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, << II << SourceRange(TemplateParams->getTemplateLoc(), TemplateParams->getRAngleLoc()); - isExplicitSpecialization = true; } } @@ -4252,6 +4252,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getSourceRange().getBegin(), + D.getIdentifierLoc(), D.getCXXScopeSpec(), TemplateParamLists.get(), TemplateParamLists.size(), @@ -6650,7 +6651,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (TemplateParameterLists.size() > 0 || (SS.isNotEmpty() && TUK != TUK_Reference)) { if (TemplateParameterList *TemplateParams - = MatchTemplateParametersToScopeSpecifier(KWLoc, SS, + = MatchTemplateParametersToScopeSpecifier(KWLoc, NameLoc, SS, TemplateParameterLists.get(), TemplateParameterLists.size(), TUK == TUK_Friend, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index c502f18c0f..04d5d4ab7e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -7259,7 +7259,7 @@ Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, bool Invalid = false; if (TemplateParameterList *TemplateParams - = MatchTemplateParametersToScopeSpecifier(TagLoc, SS, + = MatchTemplateParametersToScopeSpecifier(TagLoc, NameLoc, SS, TempParamLists.get(), TempParamLists.size(), /*friend*/ true, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 09431650f2..d76ca76693 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1433,16 +1433,34 @@ struct DependencyChecker : RecursiveASTVisitor { }; } -/// Determines whether a template-id depends on the given parameter +/// Determines whether a given type depends on the given parameter /// list. static bool -DependsOnTemplateParameters(const TemplateSpecializationType *TemplateId, - TemplateParameterList *Params) { +DependsOnTemplateParameters(QualType T, TemplateParameterList *Params) { DependencyChecker Checker(Params); - Checker.TraverseType(QualType(TemplateId, 0)); + Checker.TraverseType(T); return Checker.Match; } +// Find the source range corresponding to the named type in the given +// nested-name-specifier, if any. +static SourceRange getRangeOfTypeInNestedNameSpecifier(ASTContext &Context, + QualType T, + const CXXScopeSpec &SS) { + NestedNameSpecifierLoc NNSLoc(SS.getScopeRep(), SS.location_data()); + while (NestedNameSpecifier *NNS = NNSLoc.getNestedNameSpecifier()) { + if (const Type *CurType = NNS->getAsType()) { + if (Context.hasSameUnqualifiedType(T, QualType(CurType, 0))) + return NNSLoc.getTypeLoc().getSourceRange(); + } else + break; + + NNSLoc = NNSLoc.getPrefix(); + } + + return SourceRange(); +} + /// \brief Match the given template parameter lists to the given scope /// specifier, returning the template parameter list that applies to the /// name. @@ -1450,6 +1468,8 @@ DependsOnTemplateParameters(const TemplateSpecializationType *TemplateId, /// \param DeclStartLoc the start of the declaration that has a scope /// specifier or a template parameter list. /// +/// \param DeclLoc The location of the declaration itself. +/// /// \param SS the scope specifier that will be matched to the given template /// parameter lists. This scope specifier precedes a qualified name that is /// being declared. @@ -1474,6 +1494,7 @@ DependsOnTemplateParameters(const TemplateSpecializationType *TemplateId, /// itself a template). TemplateParameterList * Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, + SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateParameterList **ParamLists, unsigned NumParamLists, @@ -1481,138 +1502,236 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, bool &IsExplicitSpecialization, bool &Invalid) { IsExplicitSpecialization = false; - - // Find the template-ids that occur within the nested-name-specifier. These - // template-ids will match up with the template parameter lists. - llvm::SmallVector - TemplateIdsInSpecifier; - llvm::SmallVector - ExplicitSpecializationsInSpecifier; - for (NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep(); - NNS; NNS = NNS->getPrefix()) { - const Type *T = NNS->getAsType(); - if (!T) break; - - // C++0x [temp.expl.spec]p17: - // A member or a member template may be nested within many - // enclosing class templates. In an explicit specialization for - // such a member, the member declaration shall be preceded by a - // template<> for each enclosing class template that is - // explicitly specialized. - // - // Following the existing practice of GNU and EDG, we allow a typedef of a - // template specialization type. - while (const TypedefType *TT = dyn_cast(T)) - T = TT->getDecl()->getUnderlyingType().getTypePtr(); - - if (const TemplateSpecializationType *SpecType - = dyn_cast(T)) { - TemplateDecl *Template = SpecType->getTemplateName().getAsTemplateDecl(); - if (!Template) - continue; // FIXME: should this be an error? probably... - - if (const RecordType *Record = SpecType->getAs()) { - ClassTemplateSpecializationDecl *SpecDecl - = cast(Record->getDecl()); - // If the nested name specifier refers to an explicit specialization, - // we don't need a template<> header. - if (SpecDecl->getSpecializationKind() == TSK_ExplicitSpecialization) { - ExplicitSpecializationsInSpecifier.push_back(SpecDecl); - continue; + Invalid = false; + + // The sequence of nested types to which we will match up the template + // parameter lists. We first build this list by starting with the type named + // by the nested-name-specifier and walking out until we run out of types. + llvm::SmallVector NestedTypes; + QualType T; + if (SS.getScopeRep()) + T = QualType(SS.getScopeRep()->getAsType(), 0); + + // If we found an explicit specialization that prevents us from needing + // 'template<>' headers, this will be set to the location of that + // explicit specialization. + SourceLocation ExplicitSpecLoc; + + while (!T.isNull()) { + NestedTypes.push_back(T); + + // Retrieve the parent of a record type. + if (CXXRecordDecl *Record = T->getAsCXXRecordDecl()) { + // If this type is an explicit specialization, we're done. + if (ClassTemplateSpecializationDecl *Spec + = dyn_cast(Record)) { + if (!isa(Spec) && + Spec->getSpecializationKind() == TSK_ExplicitSpecialization) { + ExplicitSpecLoc = Spec->getLocation(); + break; } + } else if (Record->getTemplateSpecializationKind() + == TSK_ExplicitSpecialization) { + ExplicitSpecLoc = Record->getLocation(); + break; + } + + if (TypeDecl *Parent = dyn_cast(Record->getParent())) + T = Context.getTypeDeclType(Parent); + else + T = QualType(); + continue; + } + + if (const TemplateSpecializationType *TST + = T->getAs()) { + if (TemplateDecl *Template = TST->getTemplateName().getAsTemplateDecl()) { + if (TypeDecl *Parent = dyn_cast(Template->getDeclContext())) + T = Context.getTypeDeclType(Parent); + else + T = QualType(); + continue; } - - TemplateIdsInSpecifier.push_back(SpecType); } - } - - // Reverse the list of template-ids in the scope specifier, so that we can - // more easily match up the template-ids and the template parameter lists. - std::reverse(TemplateIdsInSpecifier.begin(), TemplateIdsInSpecifier.end()); - - SourceLocation FirstTemplateLoc = DeclStartLoc; - if (NumParamLists) - FirstTemplateLoc = ParamLists[0]->getTemplateLoc(); - - // Match the template-ids found in the specifier to the template parameter - // lists. - unsigned ParamIdx = 0, TemplateIdx = 0; - for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size(); - TemplateIdx != NumTemplateIds; ++TemplateIdx) { - const TemplateSpecializationType *TemplateId - = TemplateIdsInSpecifier[TemplateIdx]; - bool DependentTemplateId = TemplateId->isDependentType(); - - // In friend declarations we can have template-ids which don't - // depend on the corresponding template parameter lists. But - // assume that empty parameter lists are supposed to match this - // template-id. - if (IsFriend && ParamIdx < NumParamLists && ParamLists[ParamIdx]->size()) { - if (!DependentTemplateId || - !DependsOnTemplateParameters(TemplateId, ParamLists[ParamIdx])) - continue; + + // Look one step prior in a dependent template specialization type. + if (const DependentTemplateSpecializationType *DependentTST + = T->getAs()) { + if (NestedNameSpecifier *NNS = DependentTST->getQualifier()) + T = QualType(NNS->getAsType(), 0); + else + T = QualType(); + continue; + } + + // Look one step prior in a dependent name type. + if (const DependentNameType *DependentName = T->getAs()){ + if (NestedNameSpecifier *NNS = DependentName->getQualifier()) + T = QualType(NNS->getAsType(), 0); + else + T = QualType(); + continue; + } + + // Retrieve the parent of an enumeration type. + if (const EnumType *EnumT = T->getAs()) { + // FIXME: Forward-declared enums require a TSK_ExplicitSpecialization + // check here. + EnumDecl *Enum = EnumT->getDecl(); + + // Get to the parent type. + if (TypeDecl *Parent = dyn_cast(Enum->getParent())) + T = Context.getTypeDeclType(Parent); + else + T = QualType(); + continue; } - if (ParamIdx >= NumParamLists) { - // We have a template-id without a corresponding template parameter - // list. + T = QualType(); + } + // Reverse the nested types list, since we want to traverse from the outermost + // to the innermost while checking template-parameter-lists. + std::reverse(NestedTypes.begin(), NestedTypes.end()); + + // C++0x [temp.expl.spec]p17: + // A member or a member template may be nested within many + // enclosing class templates. In an explicit specialization for + // such a member, the member declaration shall be preceded by a + // template<> for each enclosing class template that is + // explicitly specialized. + unsigned ParamIdx = 0; + for (unsigned TypeIdx = 0, NumTypes = NestedTypes.size(); TypeIdx != NumTypes; + ++TypeIdx) { + T = NestedTypes[TypeIdx]; + + // Whether we expect a 'template<>' header. + bool NeedEmptyTemplateHeader = false; - // ...which is fine if this is a friend declaration. - if (IsFriend) { - IsExplicitSpecialization = true; - break; + // Whether we expect a template header with parameters. + bool NeedNonemptyTemplateHeader = false; + + // For a dependent type, the set of template parameters that we + // expect to see. + TemplateParameterList *ExpectedTemplateParams = 0; + + if (CXXRecordDecl *Record = T->getAsCXXRecordDecl()) { + if (ClassTemplatePartialSpecializationDecl *Partial + = dyn_cast(Record)) { + ExpectedTemplateParams = Partial->getTemplateParameters(); + NeedNonemptyTemplateHeader = true; + } else if (Record->isDependentType()) { + if (Record->getDescribedClassTemplate()) { + ExpectedTemplateParams = Record->getDescribedClassTemplate() + ->getTemplateParameters(); + NeedNonemptyTemplateHeader = true; + } + } else if (ClassTemplateSpecializationDecl *Spec + = dyn_cast(Record)) { + // C++0x [temp.expl.spec]p4: + // Members of an explicitly specialized class template are defined + // in the same manner as members of normal classes, and not using + // the template<> syntax. + if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization) + NeedEmptyTemplateHeader = true; + else + break; + } else if (Record->getTemplateSpecializationKind()) { + if (Record->getTemplateSpecializationKind() + != TSK_ExplicitSpecialization) + NeedEmptyTemplateHeader = true; + else + break; } - - if (DependentTemplateId) { - // FIXME: the location information here isn't great. - Diag(SS.getRange().getBegin(), - diag::err_template_spec_needs_template_parameters) - << QualType(TemplateId, 0) - << SS.getRange(); - Invalid = true; - } else { - Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_header) - << SS.getRange() - << FixItHint::CreateInsertion(FirstTemplateLoc, "template<> "); + } else if (const TemplateSpecializationType *TST + = T->getAs()) { + if (TemplateDecl *Template = TST->getTemplateName().getAsTemplateDecl()) { + ExpectedTemplateParams = Template->getTemplateParameters(); + NeedNonemptyTemplateHeader = true; + } + } else if (T->getAs()) { + // FIXME: We actually could/should check the template arguments here + // against the corresponding template parameter list. + NeedNonemptyTemplateHeader = false; + } + + if (NeedEmptyTemplateHeader) { + // If we're on the last of the types, and we need a 'template<>' header + // here, then it's an explicit specialization. + if (TypeIdx == NumTypes - 1) IsExplicitSpecialization = true; + + if (ParamIdx < NumParamLists) { + if (ParamLists[ParamIdx]->size() > 0) { + // The header has template parameters when it shouldn't. Complain. + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + diag::err_template_param_list_matches_nontemplate) + << T + << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), + ParamLists[ParamIdx]->getRAngleLoc()) + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + Invalid = true; + return 0; + } + + // Consume this template header. + ++ParamIdx; + continue; + } + + if (!IsFriend) { + // We don't have a template header, but we should. + SourceLocation ExpectedTemplateLoc; + if (NumParamLists > 0) + ExpectedTemplateLoc = ParamLists[0]->getTemplateLoc(); + else + ExpectedTemplateLoc = DeclStartLoc; + + Diag(DeclLoc, diag::err_template_spec_needs_header) + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS) + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); } - return 0; + + continue; } - - // Check the template parameter list against its corresponding template-id. - if (DependentTemplateId) { - TemplateParameterList *ExpectedTemplateParams = 0; - - // Are there cases in (e.g.) friends where this won't match? - if (const InjectedClassNameType *Injected - = TemplateId->getAs()) { - CXXRecordDecl *Record = Injected->getDecl(); - if (ClassTemplatePartialSpecializationDecl *Partial = - dyn_cast(Record)) - ExpectedTemplateParams = Partial->getTemplateParameters(); - else - ExpectedTemplateParams = Record->getDescribedClassTemplate() - ->getTemplateParameters(); + + if (NeedNonemptyTemplateHeader) { + // In friend declarations we can have template-ids which don't + // depend on the corresponding template parameter lists. But + // assume that empty parameter lists are supposed to match this + // template-id. + if (IsFriend && T->isDependentType()) { + if (ParamIdx < NumParamLists && + DependsOnTemplateParameters(T, ParamLists[ParamIdx])) + ExpectedTemplateParams = 0; + else + continue; } - if (ExpectedTemplateParams) - TemplateParameterListsAreEqual(ParamLists[ParamIdx], - ExpectedTemplateParams, - true, TPL_TemplateMatch); - - CheckTemplateParameterList(ParamLists[ParamIdx], 0, - TPC_ClassTemplateMember); - } else if (ParamLists[ParamIdx]->size() > 0) - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - diag::err_template_param_list_matches_nontemplate) - << TemplateId - << ParamLists[ParamIdx]->getSourceRange(); - else - IsExplicitSpecialization = true; - - ++ParamIdx; + if (ParamIdx < NumParamLists) { + // Check the template parameter list, if we can. + if (ExpectedTemplateParams && + !TemplateParameterListsAreEqual(ParamLists[ParamIdx], + ExpectedTemplateParams, + true, TPL_TemplateMatch)) + Invalid = true; + + if (!Invalid && + CheckTemplateParameterList(ParamLists[ParamIdx], 0, + TPC_ClassTemplateMember)) + Invalid = true; + + ++ParamIdx; + continue; + } + + Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) + << T + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + Invalid = true; + continue; + } } - + // If there were at least as many template-ids as there were template // parameter lists, then there are no template parameter lists remaining for // the declaration itself. @@ -1620,30 +1739,35 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, return 0; // If there were too many template parameter lists, complain about that now. - if (ParamIdx != NumParamLists - 1) { - while (ParamIdx < NumParamLists - 1) { - bool isExplicitSpecHeader = ParamLists[ParamIdx]->size() == 0; - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - isExplicitSpecHeader? diag::warn_template_spec_extra_headers - : diag::err_template_spec_extra_headers) - << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), - ParamLists[ParamIdx]->getRAngleLoc()); - - if (isExplicitSpecHeader && !ExplicitSpecializationsInSpecifier.empty()) { - Diag(ExplicitSpecializationsInSpecifier.back()->getLocation(), - diag::note_explicit_template_spec_does_not_need_header) - << ExplicitSpecializationsInSpecifier.back(); - ExplicitSpecializationsInSpecifier.pop_back(); - } - - // We have a template parameter list with no corresponding scope, which - // means that the resulting template declaration can't be instantiated - // properly (we'll end up with dependent nodes when we shouldn't). - if (!isExplicitSpecHeader) - Invalid = true; - - ++ParamIdx; + if (ParamIdx < NumParamLists - 1) { + bool HasAnyExplicitSpecHeader = false; + bool AllExplicitSpecHeaders = true; + for (unsigned I = ParamIdx; I != NumParamLists - 1; ++I) { + if (ParamLists[I]->size() == 0) + HasAnyExplicitSpecHeader = true; + else + AllExplicitSpecHeaders = false; } + + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + AllExplicitSpecHeaders? diag::warn_template_spec_extra_headers + : diag::err_template_spec_extra_headers) + << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), + ParamLists[NumParamLists - 2]->getRAngleLoc()); + + // If there was a specialization somewhere, such that 'template<>' is + // not required, and there were any 'template<>' headers, note where the + // specialization occurred. + if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader) + Diag(ExplicitSpecLoc, + diag::note_explicit_template_spec_does_not_need_header) + << NestedTypes.back(); + + // We have a template parameter list with no corresponding scope, which + // means that the resulting template declaration can't be instantiated + // properly (we'll end up with dependent nodes when we shouldn't). + if (!AllExplicitSpecHeaders) + Invalid = true; } // Return the last template parameter list, which corresponds to the @@ -4495,7 +4619,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, // friend declarations. bool Invalid = false; TemplateParameterList *TemplateParams - = MatchTemplateParametersToScopeSpecifier(TemplateNameLoc, SS, + = MatchTemplateParametersToScopeSpecifier(TemplateNameLoc, + TemplateNameLoc, + SS, (TemplateParameterList**)TemplateParameterLists.get(), TemplateParameterLists.size(), TUK == TUK_Friend, diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/examples.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/examples.cpp new file mode 100644 index 0000000000..733aaee754 --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.expl.spec/examples.cpp @@ -0,0 +1,167 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace PR5907 { + template struct identity { typedef T type; }; + struct A { A(); }; + identity::type::A() { } + + struct B { void f(); }; + template struct C { typedef B type; }; + + void C::type::f() { } +} + +namespace PR9421 { + namespace N { template struct S { void f(); }; } + typedef N::S T; + namespace N { template<> void T::f() {} } +} + +namespace PR8277 { + template< typename S > + struct C + { + template< int > + void F( void ) + { + } + }; + + template< typename S > + struct D + { + typedef C< int > A; + }; + + typedef D< int >::A A; + + template<> + template<> + void A::F< 0 >( void ) + { + } +} + +namespace PR8277b { + template struct C { + void f(); + }; + template struct D { + typedef C A; + }; + template<> void D::A::f() { + } +} + +namespace PR8708 { + template struct A { + template struct B { + // #2 + void f(); + }; + }; + + // #A specialize the member template for + // implicit instantiation of A, + // leaving the member template "unspecialized" + // (14.7.3/16). Specialization uses the syntax + // for explicit specialization (14.7.3/14) + template<> template + struct A::B { + // #1 + void g(); + }; + + // #1 define its function g. There is an enclosing + // class template, so we write template<> for each + // specialized template (14.7.3/15). + template<> template + void A::B::g() { } + + // #2 define the unspecialized member template's + // f + template template + void A::B::f() { } + + + // specialize the member template again, now + // specializing the member too. This specializes + // #A + template<> template<> + struct A::B { + // #3 + void h(); + }; + + // defines #3. There is no enclosing class template, so + // we write no "template<>". + void A::B::h() { } + + void test() { + // calls #1 + A::B a; a.g(); + + // calls #2 + A::B b; b.f(); + + // calls #3 + A::B c; c.h(); + } +} + +namespace PR9482 { + namespace N1 { + template struct S { + void foo() {} + }; + } + + namespace N2 { + typedef N1::S X; + } + + namespace N1 { + template<> void N2::X::foo() {} + } +} + +namespace PR9668 { + namespace First + { + template + class Bar + { + protected: + + static const bool static_bool; + }; + } + + namespace Second + { + class Foo; + } + + typedef First::Bar Special; + + namespace + First + { + template<> + const bool Special::static_bool(false); + } +} + +namespace PR9877 { + template + struct X + { + struct Y; + }; + + template<> struct X<0>::Y { static const int Z = 1; }; + template<> struct X<1>::Y { static const int Z = 1; }; + + const int X<0>::Y::Z; + template<> const int X<1>::Y::Z; // expected-error{{extraneous 'template<>' in declaration of variable 'Z'}} +} diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp index 88cfc5d7c3..56231e2f72 100644 --- a/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp +++ b/test/CXX/temp/temp.spec/temp.expl.spec/p17.cpp @@ -24,12 +24,11 @@ namespace test1 { template <> class A { public: - static int foo; // expected-note{{attempt to specialize}} + static int foo; static int bar; }; typedef A AB; - template <> int AB::foo = 0; // expected-error{{extraneous 'template<>'}} \ - // expected-error{{does not specialize}} + template <> int AB::foo = 0; // expected-error{{extraneous 'template<>'}} int AB::bar = 1; }