From 548886518d08649d72542babbe74f73b42d2d232 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 7 Oct 2009 00:13:32 +0000 Subject: [PATCH] Refactor checking of the scope of explicit template specialization declarations and explicit template instantiations, improving diagnostics and making the code usable for function template specializations (as well as class template specializations and partial specializations). llvm-svn: 83436 --- .../clang/Basic/DiagnosticSemaKinds.td | 44 ++-- clang/lib/Sema/Sema.h | 7 - clang/lib/Sema/SemaTemplate.cpp | 233 +++++++++++------- .../CXX/temp/temp.spec/temp.expl.spec/p1.cpp | 4 +- .../CXX/temp/temp.spec/temp.expl.spec/p2.cpp | 114 +++++++++ .../test/SemaTemplate/class-template-spec.cpp | 4 +- .../test/SemaTemplate/temp_class_spec_neg.cpp | 4 +- 7 files changed, 297 insertions(+), 113 deletions(-) create mode 100644 clang/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b5672fd7ca83..c0a8651b36ac 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -916,6 +916,34 @@ def err_template_arg_not_pointer_to_member_form : Error< def err_template_arg_extra_parens : Error< "non-type template argument cannot be surrounded by parentheses">; +// C++ template specialization +def err_template_spec_unknown_kind : Error< + "can only provide an explicit %select{||specialization|" + "instantiation|instantiation}0 for a class template, function template, or " + "a member function, static data member, or member class of a class template">; +def note_specialized_entity : Note< + "explicit %select{||specialized|instantiated|instantiated}0 is " + "here">; +def err_template_spec_decl_function_scope : Error< + "explicit %select{||specialization|instantiation|" + "instantiation}0 of %1 in function scope">; +def err_template_spec_decl_out_of_scope_global : Error< + "%select{class template|class template partial|function template|member " + "function|static data member|member class}0 specialization of %1 must " + "originally be declared in the global scope">; +def err_template_spec_decl_out_of_scope : Error< + "%select{class template|class template partial|function template|member " + "function|static data member|member class}0 specialization of %1 must " + "originally be declared in namespace %2">; +def err_template_spec_redecl_out_of_scope : Error< + "%select{class template|class template partial|function template|member " + "function|static data member|member class}0 specialization of %1 not in a " + "namespace enclosing %2">; +def err_template_spec_redecl_global_scope : Error< + "%select{class template|class template partial|function template|member " + "function|static data member|member class}0 specialization of %1 must occur " + "at global scope">; + // C++ class template specializations and out-of-line definitions def err_template_spec_needs_header : Error< "template specialization requires 'template<>'">; @@ -931,20 +959,6 @@ def err_template_spec_extra_headers : Error< 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_template_spec_decl_out_of_scope_global : Error< - "class template %select{|partial }0specialization of %1 must occur in the " - "global scope">; -def err_template_spec_decl_out_of_scope : Error< - "class template %select{|partial }0specialization of %1 not in namespace %2">; -def err_template_spec_decl_function_scope : Error< - "%select{class template specialization|class template partial specialization|" - "explicit instantiation}0 of %1 in function scope">; -def err_template_spec_redecl_out_of_scope : Error< - "%select{class template specialization|class template partial specialization|" - "explicit instantiation}0 of %1 not in a namespace enclosing %2">; -def err_template_spec_redecl_global_scope : Error< - "%select{class template specialization|class template partial specialization|" - "explicit instantiation}0 of %1 must occur at global scope">; // C++ Class Template Partial Specialization def err_default_arg_in_partial_spec : Error< @@ -978,7 +992,7 @@ def err_function_template_spec_ambiguous : Error< "arguments to identify a particular function template">; def note_function_template_spec_matched : Note< "function template matches specialization %0">; - + // C++ Template Instantiation def err_template_recursion_depth_exceeded : Error< "recursive template instantiation exceeded maximum depth of %0">, diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 9ddabde869b4..2056d39132e2 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2485,13 +2485,6 @@ public: const CXXScopeSpec &SS, TypeTy *ObjectType); - bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, - ClassTemplateSpecializationDecl *PrevDecl, - SourceLocation TemplateNameLoc, - SourceRange ScopeSpecifierRange, - bool PartialSpecialization, - bool ExplicitInstantiation); - bool CheckClassTemplatePartialSpecializationArgs( TemplateParameterList *TemplateParams, const TemplateArgumentListBuilder &TemplateArgs, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 02c3e43b333e..1addd16bdf4d 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2345,21 +2345,76 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { << TemplateParams->getSourceRange(); } -/// \brief Check whether a class template specialization or explicit -/// instantiation in the current context is well-formed. +/// \brief Determine what kind of template specialization the given declaration +/// is. +static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) { + if (!D) + return TSK_Undeclared; + + if (ClassTemplateSpecializationDecl *CTS + = dyn_cast(D)) + return CTS->getSpecializationKind(); + if (FunctionDecl *Function = dyn_cast(D)) + return Function->getTemplateSpecializationKind(); + + // FIXME: static data members! + // FIXME: member classes of class templates! + return TSK_Undeclared; +} + +/// \brief Check whether a specialization or explicit instantiation is +/// well-formed in the current context. /// -/// This routine determines whether a class template specialization or +/// This routine determines whether a template specialization or /// explicit instantiation can be declared in the current context -/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2) and emits -/// appropriate diagnostics if there was an error. It returns true if -// there was an error that we cannot recover from, and false otherwise. -bool -Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, - ClassTemplateSpecializationDecl *PrevDecl, - SourceLocation TemplateNameLoc, - SourceRange ScopeSpecifierRange, - bool PartialSpecialization, - bool ExplicitInstantiation) { +/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2). +/// +/// \param S the semantic analysis object for which this check is being +/// performed. +/// +/// \param Specialized the entity being specialized or instantiated, which +/// may be a kind of template (class template, function template, etc.) or +/// a member of a class template (member function, static data member, +/// member class). +/// +/// \param PrevDecl the previous declaration of this entity, if any. +/// +/// \param Loc the location of the explicit specialization or instantiation of +/// this entity. +/// +/// \param IsPartialSpecialization whether this is a partial specialization of +/// a class template. +/// +/// \param TSK the kind of specialization or implicit instantiation being +/// performed. +/// +/// \returns true if there was an error that we cannot recover from, false +/// otherwise. +static bool CheckTemplateSpecializationScope(Sema &S, + NamedDecl *Specialized, + NamedDecl *PrevDecl, + SourceLocation Loc, + bool IsPartialSpecialization, + TemplateSpecializationKind TSK) { + // Keep these "kind" numbers in sync with the %select statements in the + // various diagnostics emitted by this routine. + int EntityKind = 0; + if (isa(Specialized)) + EntityKind = IsPartialSpecialization? 1 : 0; + else if (isa(Specialized)) + EntityKind = 2; + else if (isa(Specialized)) + EntityKind = 3; + else if (isa(Specialized)) + EntityKind = 4; + else if (isa(Specialized)) + EntityKind = 5; + else { + S.Diag(Loc, diag::err_template_spec_unknown_kind) << TSK; + S.Diag(Specialized->getLocation(), diag::note_specialized_entity) << TSK; + return true; + } + // C++ [temp.expl.spec]p2: // An explicit specialization shall be declared in the namespace // of which the template is a member, or, for member templates, in @@ -2373,66 +2428,68 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, // the explicit specialization was declared, or in a namespace // that encloses the one in which the explicit specialization was // declared. - if (CurContext->getLookupContext()->isFunctionOrMethod()) { - int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0; - Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope) - << Kind << ClassTemplate; + if (S.CurContext->getLookupContext()->isFunctionOrMethod()) { + S.Diag(Loc, diag::err_template_spec_decl_function_scope) + << TSK << Specialized; return true; } - - DeclContext *DC = CurContext->getEnclosingNamespaceContext(); - DeclContext *TemplateContext - = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext(); - if ((!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) && - !ExplicitInstantiation) { - // There is no prior declaration of this entity, so this - // specialization must be in the same context as the template - // itself. - if (DC != TemplateContext) { - if (isa(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope_global) - << PartialSpecialization - << ClassTemplate << ScopeSpecifierRange; - else if (isa(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope) - << PartialSpecialization << ClassTemplate - << cast(TemplateContext) << ScopeSpecifierRange; - - Diag(ClassTemplate->getLocation(), diag::note_template_decl_here); + + bool ComplainedAboutScope = false; + DeclContext *SpecializedContext + = Specialized->getDeclContext()->getEnclosingNamespaceContext(); + if (TSK == TSK_ExplicitSpecialization) { + DeclContext *DC = S.CurContext->getEnclosingNamespaceContext(); + if ((!PrevDecl || + getTemplateSpecializationKind(PrevDecl) == TSK_Undeclared || + getTemplateSpecializationKind(PrevDecl) == TSK_ImplicitInstantiation)){ + // There is no prior declaration of this entity, so this + // specialization must be in the same context as the template + // itself. + if (!DC->Equals(SpecializedContext)) { + if (isa(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global) + << EntityKind << Specialized; + else if (isa(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_decl_out_of_scope) + << EntityKind << Specialized + << cast(SpecializedContext); + + S.Diag(Specialized->getLocation(), diag::note_template_decl_here); + ComplainedAboutScope = true; + } } - - return false; } - - // We have a previous declaration of this entity. Make sure that - // this redeclaration (or definition) occurs in an enclosing namespace. - if (!CurContext->Encloses(TemplateContext)) { - // FIXME: In C++98, we would like to turn these errors into warnings, - // dependent on a -Wc++0x flag. - bool SuppressedDiag = false; - int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0; - if (isa(TemplateContext)) { - if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope) - << Kind << ClassTemplate << ScopeSpecifierRange; - else - SuppressedDiag = true; - } else if (isa(TemplateContext)) { - if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope) - << Kind << ClassTemplate - << cast(TemplateContext) << ScopeSpecifierRange; - else - SuppressedDiag = true; - } - - if (!SuppressedDiag) - Diag(ClassTemplate->getLocation(), diag::note_template_decl_here); + + // Make sure that this redeclaration (or definition) occurs in an enclosing + // namespace. We perform this check for explicit specializations and, in + // C++0x, for explicit instantiations as well (per DR275). + // FIXME: -Wc++0x should make these warnings. + // Note that HandleDeclarator() performs this check for explicit + // specializations of function templates, static data members, and member + // functions, so we skip the check here for those kinds of entities. + // FIXME: HandleDeclarator's diagnostics aren't quite as good, though. + // Should we refactor the check, so that it occurs later? + if (!ComplainedAboutScope && !S.CurContext->Encloses(SpecializedContext) && + ((TSK == TSK_ExplicitSpecialization && + !(isa(Specialized) || isa(Specialized) || + isa(Specialized))) || + S.getLangOptions().CPlusPlus0x)) { + if (isa(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_redecl_global_scope) + << EntityKind << Specialized; + else if (isa(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_redecl_out_of_scope) + << EntityKind << Specialized + << cast(SpecializedContext); + + S.Diag(Specialized->getLocation(), diag::note_template_decl_here); } - + + // FIXME: check for specialization-after-instantiation errors and such. + return false; } - + /// \brief Check the non-type template arguments of a class template /// partial specialization according to C++ [temp.class.spec]p9. /// @@ -2714,11 +2771,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, // Check whether we can declare a class template specialization in // the current scope. if (TUK != TUK_Friend && - CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl, - TemplateNameLoc, - SS.getRange(), - isPartialSpecialization, - /*ExplicitInstantiation=*/false)) + CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl, + TemplateNameLoc, isPartialSpecialization, + TSK_ExplicitSpecialization)) return true; // The canonical type @@ -3008,6 +3063,13 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, // FIXME: Check if the prior specialization has a point of instantiation. // If so, we have run afoul of C++ [temp.expl.spec]p6. + // Check the scope of this explicit specialization. + if (CheckTemplateSpecializationScope(*this, + Specialization->getPrimaryTemplate(), + Specialization, FD->getLocation(), + false, TSK_ExplicitSpecialization)) + return true; + // Mark the prior declaration as an explicit specialization, so that later // clients know that this is an explicit specialization. // FIXME: Check for prior explicit instantiations? @@ -3071,18 +3133,10 @@ Sema::ActOnExplicitInstantiation(Scope *S, Kind = ClassTemplate->getTemplatedDecl()->getTagKind(); } - // C++0x [temp.explicit]p2: - // [...] An explicit instantiation shall appear in an enclosing - // namespace of its template. [...] - // - // This is C++ DR 275. - if (CheckClassTemplateSpecializationScope(ClassTemplate, 0, - TemplateNameLoc, - SS.getRange(), - /*PartialSpecialization=*/false, - /*ExplicitInstantiation=*/true)) - return true; - + TemplateSpecializationKind TSK + = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition + : TSK_ExplicitInstantiationDeclaration; + // Translate the parser's template argument list in our AST format. llvm::SmallVector TemplateArgs; translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs); @@ -3111,6 +3165,16 @@ Sema::ActOnExplicitInstantiation(Scope *S, ClassTemplateSpecializationDecl *PrevDecl = ClassTemplate->getSpecializations().FindNodeOrInsertPos(ID, InsertPos); + // C++0x [temp.explicit]p2: + // [...] An explicit instantiation shall appear in an enclosing + // namespace of its template. [...] + // + // This is C++ DR 275. + if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl, + TemplateNameLoc, false, + TSK)) + return true; + ClassTemplateSpecializationDecl *Specialization = 0; bool SpecializationRequiresInstantiation = true; @@ -3224,9 +3288,6 @@ Sema::ActOnExplicitInstantiation(Scope *S, // // This check comes when we actually try to perform the // instantiation. - TemplateSpecializationKind TSK - = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition - : TSK_ExplicitInstantiationDeclaration; if (SpecializationRequiresInstantiation) InstantiateClassTemplateSpecialization(Specialization, TSK); else // Instantiate the members of this class template specialization. diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp index f3d0709ef824..239b8aeb04e4 100644 --- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp +++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp @@ -1,8 +1,10 @@ -// RUN: clang-cc -fsyntax-only %s +// RUN: clang-cc -fsyntax-only -verify %s // This test creates cases where implicit instantiations of various entities // would cause a diagnostic, but provides expliict specializations for those // entities that avoid the diagnostic. The intent is to verify that +// implicit instantiations do not occur (because the explicit specialization +// is used instead). struct NonDefaultConstructible { NonDefaultConstructible(int); }; diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp new file mode 100644 index 000000000000..3bc0a07c96ee --- /dev/null +++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp @@ -0,0 +1,114 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// This test creates cases where implicit instantiations of various entities +// would cause a diagnostic, but provides expliict specializations for those +// entities that avoid the diagnostic. The specializations are alternately +// declarations and definitions, and the intent of this test is to verify +// that we allow specializations only in the appropriate namespaces (and +// nowhere else). +struct NonDefaultConstructible { + NonDefaultConstructible(int); +}; + + +// C++ [temp.expl.spec]p1: +// An explicit specialization of any of the following: + +// -- function template +namespace N0 { + template void f0(T) { // expected-note{{here}} + T t; + } + + template<> void f0(NonDefaultConstructible) { } + + void test_f0(NonDefaultConstructible NDC) { + f0(NDC); + } + + template<> void f0(int); + template<> void f0(long); +} + +template<> void N0::f0(int) { } // okay + +namespace N1 { + template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}} +} + +template<> void N0::f0(double) { } // expected-error{{originally be declared}} + +// FIXME: update the remainder of this test to check for scopes properly. +// -- class template +template +struct X0 { + static T member; + + void f1(T t) { + t = 17; + } + + struct Inner : public T { }; + + template + struct InnerTemplate : public T { }; + + template + void ft1(T t, U u); +}; + +template +template +void X0::ft1(T t, U u) { + t = u; +} + +template T X0::member; + +template<> struct X0 { }; +X0 test_X0; + + +// -- member function of a class template +template<> void X0::f1(void *) { } + +void test_spec(X0 xvp, void *vp) { + xvp.f1(vp); +} + +// -- static data member of a class template +template<> +NonDefaultConstructible X0::member = 17; + +NonDefaultConstructible &get_static_member() { + return X0::member; +} + +// -- member class of a class template +template<> +struct X0::Inner { }; + +X0::Inner inner0; + +// -- member class template of a class template +template<> +template<> +struct X0::InnerTemplate { }; + +X0::InnerTemplate inner_template0; + +// -- member function template of a class template +template<> +template<> +void X0::ft1(void*, const void*) { } + +void test_func_template(X0 xvp, void *vp, const void *cvp) { + xvp.ft1(vp, cvp); +} + +// example from the standard: +template class stream; +template<> class stream { /* ... */ }; +template class Array { /* ... */ }; +template void sort(Array& v) { /* ... */ } +template<> void sort(Array&) ; diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp index 34c616cc2f42..e4d917f7756d 100644 --- a/clang/test/SemaTemplate/class-template-spec.cpp +++ b/clang/test/SemaTemplate/class-template-spec.cpp @@ -85,12 +85,12 @@ namespace N { template<> struct N::B { }; // okay -template<> struct N::B { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}} +template<> struct N::B { }; // expected-error{{originally}} namespace M { template<> struct ::N::B { }; // expected-error{{class template specialization of 'B' not in a namespace enclosing 'N'}} - template<> struct ::A; // expected-error{{class template specialization of 'A' must occur in the global scope}} + template<> struct ::A; // expected-error{{originally}} } template<> struct N::B { diff --git a/clang/test/SemaTemplate/temp_class_spec_neg.cpp b/clang/test/SemaTemplate/temp_class_spec_neg.cpp index b50bd8f634aa..a029f474fc58 100644 --- a/clang/test/SemaTemplate/temp_class_spec_neg.cpp +++ b/clang/test/SemaTemplate/temp_class_spec_neg.cpp @@ -9,7 +9,7 @@ namespace N { } template -struct N::M::A { }; // expected-error{{not in namespace}} +struct N::M::A { }; // expected-error{{originally}} // C++ [temp.class.spec]p9 // bullet 1 @@ -25,7 +25,7 @@ template struct C; // okay template< int X, int (*array_ptr)[X] > class A2 {}; // expected-note{{here}} int array[5]; -template< int X > class A2 { }; // expected-error{{specializes}} +template< int X > class A2 { }; // expected-error{{specializes}} template class TT> struct Test0;