diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 44 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 7 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 233 | ||||
-rw-r--r-- | test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp | 4 | ||||
-rw-r--r-- | test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp | 114 | ||||
-rw-r--r-- | test/SemaTemplate/class-template-spec.cpp | 4 | ||||
-rw-r--r-- | test/SemaTemplate/temp_class_spec_neg.cpp | 4 |
7 files changed, 297 insertions, 113 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b5672fd7ca..c0a8651b36 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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{<error>|<error>|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{<error>|<error>|specialized|instantiated|instantiated}0 is " + "here">; +def err_template_spec_decl_function_scope : Error< + "explicit %select{<error>|<error>|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/lib/Sema/Sema.h b/lib/Sema/Sema.h index 9ddabde869..2056d39132 100644 --- a/lib/Sema/Sema.h +++ b/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/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 02c3e43b33..1addd16bdf 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/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<ClassTemplateSpecializationDecl>(D)) + return CTS->getSpecializationKind(); + if (FunctionDecl *Function = dyn_cast<FunctionDecl>(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<ClassTemplateDecl>(Specialized)) + EntityKind = IsPartialSpecialization? 1 : 0; + else if (isa<FunctionTemplateDecl>(Specialized)) + EntityKind = 2; + else if (isa<CXXMethodDecl>(Specialized)) + EntityKind = 3; + else if (isa<VarDecl>(Specialized)) + EntityKind = 4; + else if (isa<RecordDecl>(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<TranslationUnitDecl>(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope_global) - << PartialSpecialization - << ClassTemplate << ScopeSpecifierRange; - else if (isa<NamespaceDecl>(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope) - << PartialSpecialization << ClassTemplate - << cast<NamedDecl>(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<TranslationUnitDecl>(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global) + << EntityKind << Specialized; + else if (isa<NamespaceDecl>(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_decl_out_of_scope) + << EntityKind << Specialized + << cast<NamedDecl>(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<TranslationUnitDecl>(TemplateContext)) { - if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope) - << Kind << ClassTemplate << ScopeSpecifierRange; - else - SuppressedDiag = true; - } else if (isa<NamespaceDecl>(TemplateContext)) { - if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope) - << Kind << ClassTemplate - << cast<NamedDecl>(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<FunctionTemplateDecl>(Specialized) || isa<VarDecl>(Specialized) || + isa<FunctionDecl>(Specialized))) || + S.getLangOptions().CPlusPlus0x)) { + if (isa<TranslationUnitDecl>(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_redecl_global_scope) + << EntityKind << Specialized; + else if (isa<NamespaceDecl>(SpecializedContext)) + S.Diag(Loc, diag::err_template_spec_redecl_out_of_scope) + << EntityKind << Specialized + << cast<NamedDecl>(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<TemplateArgument, 16> 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/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp index f3d0709ef8..239b8aeb04 100644 --- a/test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp +++ b/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/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp new file mode 100644 index 0000000000..3bc0a07c96 --- /dev/null +++ b/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<typename T> 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<typename T> +struct X0 { + static T member; + + void f1(T t) { + t = 17; + } + + struct Inner : public T { }; + + template<typename U> + struct InnerTemplate : public T { }; + + template<typename U> + void ft1(T t, U u); +}; + +template<typename T> +template<typename U> +void X0<T>::ft1(T t, U u) { + t = u; +} + +template<typename T> T X0<T>::member; + +template<> struct X0<void> { }; +X0<void> test_X0; + + +// -- member function of a class template +template<> void X0<void*>::f1(void *) { } + +void test_spec(X0<void*> xvp, void *vp) { + xvp.f1(vp); +} + +// -- static data member of a class template +template<> +NonDefaultConstructible X0<NonDefaultConstructible>::member = 17; + +NonDefaultConstructible &get_static_member() { + return X0<NonDefaultConstructible>::member; +} + +// -- member class of a class template +template<> +struct X0<void*>::Inner { }; + +X0<void*>::Inner inner0; + +// -- member class template of a class template +template<> +template<> +struct X0<void*>::InnerTemplate<int> { }; + +X0<void*>::InnerTemplate<int> inner_template0; + +// -- member function template of a class template +template<> +template<> +void X0<void*>::ft1(void*, const void*) { } + +void test_func_template(X0<void *> xvp, void *vp, const void *cvp) { + xvp.ft1(vp, cvp); +} + +// example from the standard: +template<class T> class stream; +template<> class stream<char> { /* ... */ }; +template<class T> class Array { /* ... */ }; +template<class T> void sort(Array<T>& v) { /* ... */ } +template<> void sort<char*>(Array<char*>&) ; diff --git a/test/SemaTemplate/class-template-spec.cpp b/test/SemaTemplate/class-template-spec.cpp index 34c616cc2f..e4d917f775 100644 --- a/test/SemaTemplate/class-template-spec.cpp +++ b/test/SemaTemplate/class-template-spec.cpp @@ -85,12 +85,12 @@ namespace N { template<> struct N::B<int> { }; // okay -template<> struct N::B<float> { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}} +template<> struct N::B<float> { }; // expected-error{{originally}} namespace M { template<> struct ::N::B<short> { }; // expected-error{{class template specialization of 'B' not in a namespace enclosing 'N'}} - template<> struct ::A<long double>; // expected-error{{class template specialization of 'A' must occur in the global scope}} + template<> struct ::A<long double>; // expected-error{{originally}} } template<> struct N::B<char> { diff --git a/test/SemaTemplate/temp_class_spec_neg.cpp b/test/SemaTemplate/temp_class_spec_neg.cpp index b50bd8f634..a029f474fc 100644 --- a/test/SemaTemplate/temp_class_spec_neg.cpp +++ b/test/SemaTemplate/temp_class_spec_neg.cpp @@ -9,7 +9,7 @@ namespace N { } template<typename T> -struct N::M::A<T*> { }; // expected-error{{not in namespace}} +struct N::M::A<T*> { }; // expected-error{{originally}} // C++ [temp.class.spec]p9 // bullet 1 @@ -25,7 +25,7 @@ template <class T, T* t> struct C<T*, t>; // okay template< int X, int (*array_ptr)[X] > class A2 {}; // expected-note{{here}} int array[5]; -template< int X > class A2<X,&array> { }; // expected-error{{specializes}} +template< int X > class A2<X, &array> { }; // expected-error{{specializes}} template<typename T, int N, template<typename X> class TT> struct Test0; |