diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e7c9a16972f3..38c011dcc5c2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1893,6 +1893,9 @@ def note_explicit_template_spec_does_not_need_header : Note< def err_template_qualified_declarator_no_match : Error< "nested name specifier '%0' for declaration does not refer into a class, " "class template or class template partial specialization">; +def err_specialize_member_of_template : Error< + "cannot specialize (with 'template<>') a member of an unspecialized " + "template">; // C++ Class Template Partial Specialization def err_default_arg_in_partial_spec : Error< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5de1838b9f74..e1872c2d7dbf 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4516,8 +4516,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (!getLangOptions().CPlusPlus) { // Perform semantic checking on the function declaration. - bool isExplctSpecialization=false; - CheckFunctionDeclaration(S, NewFD, Previous, isExplctSpecialization, + bool isExplicitSpecialization=false; + CheckFunctionDeclaration(S, NewFD, Previous, isExplicitSpecialization, Redeclaration); assert((NewFD->isInvalidDecl() || !Redeclaration || Previous.getResultKind() != LookupResult::FoundOverloaded) && @@ -4540,7 +4540,9 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, HasExplicitTemplateArgs = true; - if (FunctionTemplate) { + if (NewFD->isInvalidDecl()) { + HasExplicitTemplateArgs = false; + } else if (FunctionTemplate) { // Function template with explicit template arguments. Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec) << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 3761740d8c03..8213f3266f1f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1610,6 +1610,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, // such a member, the member declaration shall be preceded by a // template<> for each enclosing class template that is // explicitly specialized. + bool SawNonEmptyTemplateParameterList = false; unsigned ParamIdx = 0; for (unsigned TypeIdx = 0, NumTypes = NestedTypes.size(); TypeIdx != NumTypes; ++TypeIdx) { @@ -1671,6 +1672,26 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, NeedNonemptyTemplateHeader = false; } + // C++ [temp.expl.spec]p16: + // In an explicit specialization declaration for a member of a class + // template or a member template that ap- pears in namespace scope, the + // member template and some of its enclosing class templates may remain + // unspecialized, except that the declaration shall not explicitly + // specialize a class member template if its en- closing class templates + // are not explicitly specialized as well. + if (ParamIdx < NumParamLists) { + if (ParamLists[ParamIdx]->size() == 0) { + if (SawNonEmptyTemplateParameterList) { + Diag(DeclLoc, diag::err_specialize_member_of_template) + << ParamLists[ParamIdx]->getSourceRange(); + Invalid = true; + IsExplicitSpecialization = false; + return 0; + } + } else + SawNonEmptyTemplateParameterList = true; + } + if (NeedEmptyTemplateHeader) { // If we're on the last of the types, and we need a 'template<>' header // here, then it's an explicit specialization. @@ -1787,6 +1808,22 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, Invalid = true; } + // C++ [temp.expl.spec]p16: + // In an explicit specialization declaration for a member of a class + // template or a member template that ap- pears in namespace scope, the + // member template and some of its enclosing class templates may remain + // unspecialized, except that the declaration shall not explicitly + // specialize a class member template if its en- closing class templates + // are not explicitly specialized as well. + if (ParamLists[NumParamLists - 1]->size() == 0 && + SawNonEmptyTemplateParameterList) { + Diag(DeclLoc, diag::err_specialize_member_of_template) + << ParamLists[ParamIdx]->getSourceRange(); + Invalid = true; + IsExplicitSpecialization = false; + return 0; + } + // Return the last template parameter list, which corresponds to the // entity being declared. return ParamLists[NumParamLists - 1]; diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p16.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p16.cpp index 2f9a3cbf9457..c7597e9f817c 100644 --- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p16.cpp +++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p16.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only %s +// RUN: %clang_cc1 -fsyntax-only -verify %s template struct A { void f(T); template void g1(T, X1); @@ -24,3 +24,15 @@ template<> template<> // member specialization even if defined in class definition template<> void A::h(int) { } + +namespace PR10024 { + template + struct Test{ + template + void get(U i) {} + }; + + template + template <> + void Test::get(double i) {} // expected-error{{cannot specialize (with 'template<>') a member of an unspecialized template}} +}