From 900d0461f297de41ad382bb003051420e878fce3 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 17 Apr 2014 03:52:20 +0000 Subject: [PATCH] PR19340: If we see a declaration of a member of an unspecialized class template that looks like it might be an explicit specialization, don't recover as an explicit specialization (bypassing the check that would reject that). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@206444 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 4 +- lib/Sema/SemaTemplate.cpp | 85 ++++++++++--------- .../explicit-specialization-member.cpp | 18 +++- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 180e088d39..c13be0326e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3267,8 +3267,8 @@ 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">; + "cannot specialize %select{|(with 'template<>') }0a member of an " + "unspecialized template">; // C++ Class Template Partial Specialization def err_default_arg_in_partial_spec : Error< diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 58742fc055..2ef144f158 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1721,6 +1721,38 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( // template<> for each enclosing class template that is // explicitly specialized. bool SawNonEmptyTemplateParameterList = false; + + auto CheckExplicitSpecialization = [&](SourceRange Range, + bool Recovery = false) { + if (SawNonEmptyTemplateParameterList) { + Diag(DeclLoc, diag::err_specialize_member_of_template) + << !Recovery << Range; + Invalid = true; + IsExplicitSpecialization = false; + return true; + } + + return false; + }; + + auto DiagnoseMissingExplicitSpecialization = [&] (SourceRange Range) { + // Check that we can have an explicit specialization here. + if (CheckExplicitSpecialization(Range, true)) + return true; + + // We don't have a template header, but we should. + SourceLocation ExpectedTemplateLoc; + if (!ParamLists.empty()) + ExpectedTemplateLoc = ParamLists[0]->getTemplateLoc(); + else + ExpectedTemplateLoc = DeclStartLoc; + + Diag(DeclLoc, diag::err_template_spec_needs_header) + << Range + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); + return false; + }; + unsigned ParamIdx = 0; for (unsigned TypeIdx = 0, NumTypes = NestedTypes.size(); TypeIdx != NumTypes; ++TypeIdx) { @@ -1791,13 +1823,8 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( // are not explicitly specialized as well. if (ParamIdx < ParamLists.size()) { if (ParamLists[ParamIdx]->size() == 0) { - if (SawNonEmptyTemplateParameterList) { - Diag(DeclLoc, diag::err_specialize_member_of_template) - << ParamLists[ParamIdx]->getSourceRange(); - Invalid = true; - IsExplicitSpecialization = false; + if (CheckExplicitSpecialization(ParamLists[ParamIdx]->getSourceRange())) return 0; - } } else SawNonEmptyTemplateParameterList = true; } @@ -1820,29 +1847,20 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( 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 (!ParamLists.empty()) - ExpectedTemplateLoc = ParamLists[0]->getTemplateLoc(); - else - ExpectedTemplateLoc = DeclStartLoc; - - // FIXME: Don't recover this way if we SawNonEmptyTemplateParameterList. - Diag(DeclLoc, diag::err_template_spec_needs_header) - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS) - << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); } - + + if (!IsFriend) + if (DiagnoseMissingExplicitSpecialization( + getRangeOfTypeInNestedNameSpecifier(Context, T, SS))) + return 0; + continue; } - + if (NeedNonemptyTemplateHeader) { // In friend declarations we can have template-ids which don't // depend on the corresponding template parameter lists. But @@ -1886,18 +1904,11 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( // the declaration itself. if (ParamIdx >= ParamLists.size()) { if (TemplateId && !IsFriend) { - // FIXME: Don't recover this way if we SawNonEmptyTemplateParameterList. // We don't have a template header for the declaration itself, but we // should. - SourceLocation ExpectedTemplateLoc; - if (!ParamLists.empty()) - ExpectedTemplateLoc = ParamLists[0]->getTemplateLoc(); - else - ExpectedTemplateLoc = DeclStartLoc; - Diag(DeclLoc, diag::err_template_spec_needs_header) - << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc) - << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); IsExplicitSpecialization = true; + DiagnoseMissingExplicitSpecialization(SourceRange(TemplateId->LAngleLoc, + TemplateId->RAngleLoc)); // Fabricate an empty template parameter list for the invented header. return TemplateParameterList::Create(Context, SourceLocation(), @@ -1947,14 +1958,10 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( // 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.back()->size() == 0 && SawNonEmptyTemplateParameterList) { - Diag(DeclLoc, diag::err_specialize_member_of_template) - << ParamLists[ParamIdx]->getSourceRange(); - Invalid = true; - IsExplicitSpecialization = false; + if (ParamLists.back()->size() == 0 && + CheckExplicitSpecialization(ParamLists[ParamIdx]->getSourceRange())) return 0; - } - + // Return the last template parameter list, which corresponds to the // entity being declared. return ParamLists.back(); diff --git a/test/SemaTemplate/explicit-specialization-member.cpp b/test/SemaTemplate/explicit-specialization-member.cpp index 07d7389446..e8dc4d219a 100644 --- a/test/SemaTemplate/explicit-specialization-member.cpp +++ b/test/SemaTemplate/explicit-specialization-member.cpp @@ -38,12 +38,22 @@ namespace PR18246 { template template - void Baz::bar() { + void Baz::bar() { // expected-note {{couldn't infer template argument 'N'}} } - // FIXME: Don't suggest the 'template<>' correction here, because this cannot - // be an explicit specialization. + // FIXME: We shouldn't try to match this against a prior declaration if + // template parameter matching failed. template - void Baz::bar<0>() { // expected-error {{requires 'template<>'}} + void Baz::bar<0>() { // expected-error {{cannot specialize a member of an unspecialized template}} \ + // expected-error {{no function template matches}} } } + +namespace PR19340 { +template struct Helper { + template static void func(const T *m) {} // expected-note {{failed template argument deduction}} +}; + +template void Helper::func<2>() {} // expected-error {{cannot specialize a member}} \ + // expected-error {{no function template matches}} +} -- 2.40.0