From 88b7094185b9d4fe9820c731b6936d8d37f6143e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 25 Feb 2009 22:02:03 +0000 Subject: [PATCH] Perform additional semantic checking of class template specializations. In particular: - Make sure class template specializations have a "template<>" header, and complain if they don't. - Make sure class template specializations are declared/defined within a valid context. (e.g., you can't declare a specialization std::vector in the global namespace). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65476 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclBase.h | 6 ++ include/clang/Basic/DiagnosticSemaKinds.def | 18 ++++ lib/AST/DeclBase.cpp | 8 ++ lib/Sema/Sema.h | 6 ++ lib/Sema/SemaTemplate.cpp | 113 ++++++++++++++++++-- test/SemaTemplate/class-template-spec.cpp | 44 ++++++-- 6 files changed, 173 insertions(+), 22 deletions(-) diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 312cdf093c..1357a8eaac 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -490,6 +490,12 @@ public: } const DeclContext *getLookupContext() const; + /// \brief Retrieve the nearest enclosing namespace context. + DeclContext *getEnclosingNamespaceContext(); + const DeclContext *getEnclosingNamespaceContext() const { + return const_cast(this)->getEnclosingNamespaceContext(); + } + /// getNextContext - If this is a DeclContext that may have other /// DeclContexts that are semantically connected but syntactically /// different, such as C++ namespaces, this routine retrieves the diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index e5e7f6eacf..5cd8c6be96 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -605,6 +605,24 @@ DIAG(err_template_arg_not_pointer_to_member_form, ERROR, DIAG(err_template_arg_extra_parens, ERROR, "non-type template argument cannot be surrounded by parentheses") +// C++ class template specialization +DIAG(err_template_spec_needs_header, ERROR, + "template specialization requires 'template<>'") +DIAG(err_template_spec_extra_headers, ERROR, + "template specialization must have a single 'template<>' header") +DIAG(unsup_template_partial_spec, ERROR, + "class template partial specialization is not yet supported") +DIAG(err_template_spec_decl_out_of_scope_global, ERROR, + "class template specialization of %0 must occur in the global scope") +DIAG(err_template_spec_decl_out_of_scope, ERROR, + "class template specialization of %0 not in namespace %1") +DIAG(err_template_spec_decl_function_scope, ERROR, + "class template specialization of %0 in function scope") +DIAG(err_template_spec_redecl_out_of_scope, ERROR, + "class template specialization of %0 not in a namespace enclosing %1") +DIAG(err_template_spec_redecl_global_scope, ERROR, + "class template specialization of %0 must occur in at global scope") + DIAG(err_unexpected_typedef, ERROR, "unexpected type name %0: expected expression") DIAG(err_unexpected_namespace, ERROR, diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 18712cde35..20139d7bb2 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -555,6 +555,14 @@ const DeclContext *DeclContext::getLookupContext() const { return Ctx; } +DeclContext *DeclContext::getEnclosingNamespaceContext() { + DeclContext *Ctx = this; + // Skip through non-namespace, non-translation-unit contexts. + while (!Ctx->isFileContext() || Ctx->isTransparentContext()) + Ctx = Ctx->getParent(); + return Ctx->getPrimaryContext(); +} + void DeclContext::makeDeclVisibleInContext(NamedDecl *D) { // FIXME: This feels like a hack. Should DeclarationName support // template-ids, or is there a better way to keep specializations diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index fed2bbdf7c..1d36d46296 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -64,6 +64,7 @@ namespace clang { class TemplateArgument; class TemplateParameterList; class TemplateTemplateParmDecl; + class ClassTemplateDecl; class ObjCInterfaceDecl; class ObjCCompatibleAliasDecl; class ObjCProtocolDecl; @@ -1532,6 +1533,11 @@ public: SourceLocation RAngleLoc, const CXXScopeSpec *SS); + bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, + ClassTemplateSpecializationDecl *PrevDecl, + SourceLocation TemplateNameLoc, + SourceRange ScopeSpecifierRange); + virtual DeclTy * ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index e3538e8a4c..db582edba2 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1520,6 +1520,77 @@ Sema::CheckTemplateDeclScope(Scope *S, << TemplateRange; } +/// \brief Check whether a class template specialization in the +/// current context is well-formed. +/// +/// This routine determines whether a class template specialization +/// can be declared in the current context (C++ [temp.expl.spec]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) { + // 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 + // the namespace of which the enclosing class or enclosing class + // template is a member. An explicit specialization of a member + // function, member class or static data member of a class + // template shall be declared in the namespace of which the class + // template is a member. Such a declaration may also be a + // definition. If the declaration is not a definition, the + // specialization may be defined later in the name- space in which + // the explicit specialization was declared, or in a namespace + // that encloses the one in which the explicit specialization was + // declared. + if (CurContext->getLookupContext()->isFunctionOrMethod()) { + Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope) + << ClassTemplate; + return true; + } + + DeclContext *DC = CurContext->getEnclosingNamespaceContext(); + DeclContext *TemplateContext + = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext(); + if (!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) { + // 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) + << ClassTemplate << ScopeSpecifierRange; + else if (isa(TemplateContext)) + Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope) + << ClassTemplate << cast(TemplateContext) + << ScopeSpecifierRange; + + Diag(ClassTemplate->getLocation(), diag::note_template_decl_here); + } + + 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)) { + if (isa(TemplateContext)) + Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope) + << ClassTemplate << ScopeSpecifierRange; + else if (isa(TemplateContext)) + Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope) + << ClassTemplate << cast(TemplateContext) + << ScopeSpecifierRange; + + Diag(ClassTemplate->getLocation(), diag::note_template_decl_here); + } + + return false; +} + Sema::DeclTy * Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, @@ -1532,23 +1603,36 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation RAngleLoc, AttributeList *Attr, MultiTemplateParamsArg TemplateParameterLists) { - // FIXME: We need to match up the scope-specifier with the template - // parameter lists, and will eventually end up with a single - // template parameter list remaining (which applies to - // TemplateIdType). - assert(TemplateParameterLists.size() == 1 && - "Clang doesn't handle with ill-formed specializations yet."); - - assert(static_cast(*TemplateParameterLists.get()) - ->size() == 0 && - "Clang doesn't handle class template partial specializations yet"); - // Find the class template we're specializing ClassTemplateDecl *ClassTemplate = dyn_cast_or_null(static_cast(TemplateD)); if (!ClassTemplate) return 0; + // Check the validity of the template headers that introduce this + // template. + if (TemplateParameterLists.size() == 0) { + // FIXME: It would be very nifty if we could introduce some kind + // of "code insertion hint" that could show the code that needs to + // be added. + Diag(KWLoc, diag::err_template_spec_needs_header); + } else { + TemplateParameterList *TemplateParams + = static_cast(*TemplateParameterLists.get()); + if (TemplateParameterLists.size() > 1) { + Diag(TemplateParams->getTemplateLoc(), + diag::err_template_spec_extra_headers); + return 0; + } + + if (TemplateParams->size() > 0) { + // FIXME: No support for class template partial specialization. + Diag(TemplateParams->getTemplateLoc(), + diag::unsup_template_partial_spec); + return 0; + } + } + // Check that the specialization uses the same tag kind as the // original template. TagDecl::TagKind Kind; @@ -1588,6 +1672,13 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, ClassTemplateSpecializationDecl *Specialization = 0; + // Check whether we can declare a class template specialization in + // the current scope. + if (CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl, + TemplateNameLoc, + SS.getRange())) + return 0; + if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) { // Since the only prior class template specialization with these // arguments was referenced but not declared, reuse that diff --git a/test/SemaTemplate/class-template-spec.cpp b/test/SemaTemplate/class-template-spec.cpp index 4de220a77c..4ae1b9bdc9 100644 --- a/test/SemaTemplate/class-template-spec.cpp +++ b/test/SemaTemplate/class-template-spec.cpp @@ -1,13 +1,13 @@ // RUN: clang -fsyntax-only -verify %s -template class A; +template struct A; // expected-note{{template is declared here}} -template<> class A; // expected-note{{forward declaration}} +template<> struct A; // expected-note{{forward declaration}} -template<> class A { // expected-note{{previous definition}} +template<> struct A { // expected-note{{previous definition}} int x; }; -template<> class A { // expected-note{{previous definition}} +template<> struct A { // expected-note{{previous definition}} int y; }; @@ -24,16 +24,16 @@ int test_incomplete_specs(A *a1, typedef float FLOAT; -template<> class A; +template<> struct A; -template<> class A { }; // expected-error{{redefinition}} +template<> struct A { }; // expected-error{{redefinition}} -template<> class A { }; // expected-error{{redefinition}} +template<> struct A { }; // expected-error{{redefinition}} -template class X; +template struct X; -template <> class X { int foo(); }; // #1 -template <> class X { int bar(); }; // #2 +template <> struct X { int foo(); }; // #1 +template <> struct X { int bar(); }; // #2 typedef int int_type; void testme(X *x1, X *x2) { @@ -42,4 +42,26 @@ void testme(X *x1, X *x2) { } // Diagnose specializations in a different namespace -class A { }; +struct A { }; // expected-error{{template specialization requires 'template<>'}} + +template // expected-error{{class template partial specialization is not yet supported}} +struct A { }; + +template<> struct ::A; + +namespace N { + template struct B; // expected-note 2{{template is declared here}} + + template<> struct ::N::B; // okay + template<> struct ::N::B; // okay +} + +template<> struct N::B { }; // okay + +template<> struct N::B { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}} + +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}} +} -- 2.40.0