diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-02-06 22:42:48 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-02-06 22:42:48 +0000 |
commit | ddc29e116db3c3f4144355e67a0137b38b6bb6d1 (patch) | |
tree | add7fd5470501bcc7a612dac9fbd03b5918b86da /lib/Sema/SemaTemplate.cpp | |
parent | 4398a78095cd05a3be702fbab25bfe324a5d7946 (diff) |
Semantic checking for class template declarations and
redeclarations. For example, checks that a class template
redeclaration has the same template parameters as previous
declarations.
Detangled class-template checking from ActOnTag, whose logic was
getting rather convoluted because it tried to handle C, C++, and C++
template semantics in one shot.
Made some inroads toward eliminating extraneous "declaration does not
declare anything" errors by adding an "error" type specifier.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63973 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaTemplate.cpp')
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 269 |
1 files changed, 267 insertions, 2 deletions
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index e15343a042..6c5f90afa5 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -12,6 +12,7 @@ //+//===----------------------------------------------------------------------===/ #include "Sema.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/DeclTemplate.h" #include "clang/Parse/DeclSpec.h" @@ -115,8 +116,12 @@ Sema::DeclTy *Sema::ActOnTypeParameter(Scope *S, bool Typename, PrevDecl); } + SourceLocation Loc = ParamNameLoc; + if (!ParamName) + Loc = KeyLoc; + TemplateTypeParmDecl *Param - = TemplateTypeParmDecl::Create(Context, CurContext, ParamNameLoc, + = TemplateTypeParmDecl::Create(Context, CurContext, Loc, Depth, Position, ParamName, Typename); if (Invalid) Param->setInvalidDecl(); @@ -217,6 +222,266 @@ Sema::ActOnTemplateParameterList(unsigned Depth, if (ExportLoc.isValid()) Diag(ExportLoc, diag::note_template_export_unsupported); - return TemplateParameterList::Create(Context, (Decl**)Params, NumParams); + return TemplateParameterList::Create(Context, TemplateLoc, LAngleLoc, + (Decl**)Params, NumParams, RAngleLoc); +} + +Sema::DeclTy * +Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, + SourceLocation KWLoc, const CXXScopeSpec &SS, + IdentifierInfo *Name, SourceLocation NameLoc, + AttributeList *Attr, + MultiTemplateParamsArg TemplateParameterLists) { + assert(TemplateParameterLists.size() > 0 && "No template parameter lists?"); + assert(TK != TK_Reference && "Can only declare or define class templates"); + + // Check that we can declare a template here. + if (CheckTemplateDeclScope(S, TemplateParameterLists)) + return 0; + + TagDecl::TagKind Kind; + switch (TagSpec) { + default: assert(0 && "Unknown tag type!"); + case DeclSpec::TST_struct: Kind = TagDecl::TK_struct; break; + case DeclSpec::TST_union: Kind = TagDecl::TK_union; break; + case DeclSpec::TST_class: Kind = TagDecl::TK_class; break; + } + + // There is no such thing as an unnamed class template. + if (!Name) { + Diag(KWLoc, diag::err_template_unnamed_class); + return 0; + } + + // Find any previous declaration with this name. + LookupResult Previous = LookupParsedName(S, &SS, Name, LookupOrdinaryName, + true); + assert(!Previous.isAmbiguous() && "Ambiguity in class template redecl?"); + NamedDecl *PrevDecl = 0; + if (Previous.begin() != Previous.end()) + PrevDecl = *Previous.begin(); + + DeclContext *SemanticContext = CurContext; + if (SS.isNotEmpty() && !SS.isInvalid()) { + SemanticContext = static_cast<DeclContext*>(SS.getScopeRep()); + + // FIXME: need to match up several levels of template parameter + // lists here. + } + + // FIXME: member templates! + TemplateParameterList *TemplateParams + = static_cast<TemplateParameterList *>(*TemplateParameterLists.release()); + + // If there is a previous declaration with the same name, check + // whether this is a valid redeclaration. + ClassTemplateDecl *PrevClassTemplate + = dyn_cast_or_null<ClassTemplateDecl>(PrevDecl); + if (PrevClassTemplate) { + // Ensure that the template parameter lists are compatible. + if (!TemplateParameterListsAreEqual(TemplateParams, + PrevClassTemplate->getTemplateParameters(), + /*Complain=*/true)) + return 0; + + // C++ [temp.class]p4: + // In a redeclaration, partial specialization, explicit + // specialization or explicit instantiation of a class template, + // the class-key shall agree in kind with the original class + // template declaration (7.1.5.3). + RecordDecl *PrevRecordDecl = PrevClassTemplate->getTemplatedDecl(); + if (PrevRecordDecl->getTagKind() != Kind) { + Diag(KWLoc, diag::err_use_with_wrong_tag) << Name; + Diag(PrevRecordDecl->getLocation(), diag::note_previous_use); + return 0; + } + + + // Check for redefinition of this class template. + if (TK == TK_Definition) { + if (TagDecl *Def = PrevRecordDecl->getDefinition(Context)) { + Diag(NameLoc, diag::err_redefinition) << Name; + Diag(Def->getLocation(), diag::note_previous_definition); + // FIXME: Would it make sense to try to "forget" the previous + // definition, as part of error recovery? + return 0; + } + } + } else if (PrevDecl && PrevDecl->isTemplateParameter()) { + // Maybe we will complain about the shadowed template parameter. + DiagnoseTemplateParameterShadow(NameLoc, PrevDecl); + // Just pretend that we didn't see the previous declaration. + PrevDecl = 0; + } else if (PrevDecl) { + // C++ [temp]p5: + // A class template shall not have the same name as any other + // template, class, function, object, enumeration, enumerator, + // namespace, or type in the same scope (3.3), except as specified + // in (14.5.4). + Diag(NameLoc, diag::err_redefinition_different_kind) << Name; + Diag(PrevDecl->getLocation(), diag::note_previous_definition); + return 0; + } + + // If we had a scope specifier, we better have a previous template + // declaration! + + TagDecl *NewClass = + CXXRecordDecl::Create(Context, Kind, SemanticContext, NameLoc, Name, + PrevClassTemplate? + PrevClassTemplate->getTemplatedDecl() : 0); + + ClassTemplateDecl *NewTemplate + = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, + DeclarationName(Name), TemplateParams, + NewClass); + + // Set the lexical context of these templates + NewClass->setLexicalDeclContext(CurContext); + NewTemplate->setLexicalDeclContext(CurContext); + + if (TK == TK_Definition) + NewClass->startDefinition(); + + if (Attr) + ProcessDeclAttributeList(NewClass, Attr); + + PushOnScopeChains(NewTemplate, S); + + return NewTemplate; } +/// \brief Determine whether the given template parameter lists are +/// equivalent. +/// +/// \param New The new template parameter list, typically written in the +/// source code as part of a new template declaration. +/// +/// \param Old The old template parameter list, typically found via +/// name lookup of the template declared with this template parameter +/// list. +/// +/// \param Complain If true, this routine will produce a diagnostic if +/// the template parameter lists are not equivalent. +/// +/// \returns True if the template parameter lists are equal, false +/// otherwise. +bool +Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, + TemplateParameterList *Old, + bool Complain, + bool IsTemplateTemplateParm) { + if (Old->size() != New->size()) { + if (Complain) { + Diag(New->getTemplateLoc(), diag::err_template_param_list_different_arity) + << (New->size() > Old->size()) + << IsTemplateTemplateParm + << SourceRange(New->getTemplateLoc(), New->getRAngleLoc()); + Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) + << IsTemplateTemplateParm + << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc()); + } + + return false; + } + + for (TemplateParameterList::iterator OldParm = Old->begin(), + OldParmEnd = Old->end(), NewParm = New->begin(); + OldParm != OldParmEnd; ++OldParm, ++NewParm) { + if ((*OldParm)->getKind() != (*NewParm)->getKind()) { + Diag((*NewParm)->getLocation(), diag::err_template_param_different_kind) + << IsTemplateTemplateParm; + Diag((*OldParm)->getLocation(), diag::note_template_prev_declaration) + << IsTemplateTemplateParm; + return false; + } + + if (isa<TemplateTypeParmDecl>(*OldParm)) { + // Okay; all template type parameters are equivalent (since we + // know we're at the same depth/level). +#ifndef NDEBUG + QualType OldParmType + = Context.getTypeDeclType(cast<TemplateTypeParmDecl>(*OldParm)); + QualType NewParmType + = Context.getTypeDeclType(cast<TemplateTypeParmDecl>(*NewParm)); + assert(Context.getCanonicalType(OldParmType) == + Context.getCanonicalType(NewParmType) && + "type parameter mismatch?"); +#endif + } else if (NonTypeTemplateParmDecl *OldNTTP + = dyn_cast<NonTypeTemplateParmDecl>(*OldParm)) { + // The types of non-type template parameters must agree. + NonTypeTemplateParmDecl *NewNTTP + = cast<NonTypeTemplateParmDecl>(*NewParm); + if (Context.getCanonicalType(OldNTTP->getType()) != + Context.getCanonicalType(NewNTTP->getType())) { + if (Complain) { + Diag(NewNTTP->getLocation(), + diag::err_template_nontype_parm_different_type) + << NewNTTP->getType() + << IsTemplateTemplateParm; + Diag(OldNTTP->getLocation(), + diag::note_template_nontype_parm_prev_declaration) + << OldNTTP->getType(); + } + return false; + } + } else { + // The template parameter lists of template template + // parameters must agree. + // FIXME: Could we perform a faster "type" comparison here? + assert(isa<TemplateTemplateParmDecl>(*OldParm) && + "Only template template parameters handled here"); + TemplateTemplateParmDecl *OldTTP + = cast<TemplateTemplateParmDecl>(*OldParm); + TemplateTemplateParmDecl *NewTTP + = cast<TemplateTemplateParmDecl>(*NewParm); + if (!TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), + OldTTP->getTemplateParameters(), + Complain, + /*IsTemplateTemplateParm=*/true)) + return false; + } + } + + return true; +} + +/// \brief Check whether a template can be declared within this scope. +/// +/// If the template declaration is valid in this scope, returns +/// false. Otherwise, issues a diagnostic and returns true. +bool +Sema::CheckTemplateDeclScope(Scope *S, + MultiTemplateParamsArg &TemplateParameterLists) { + assert(TemplateParameterLists.size() > 0 && "Not a template"); + + // Find the nearest enclosing declaration scope. + while ((S->getFlags() & Scope::DeclScope) == 0 || + (S->getFlags() & Scope::TemplateParamScope) != 0) + S = S->getParent(); + + TemplateParameterList *TemplateParams = + static_cast<TemplateParameterList*>(*TemplateParameterLists.get()); + SourceLocation TemplateLoc = TemplateParams->getTemplateLoc(); + SourceRange TemplateRange + = SourceRange(TemplateLoc, TemplateParams->getRAngleLoc()); + + // C++ [temp]p2: + // A template-declaration can appear only as a namespace scope or + // class scope declaration. + DeclContext *Ctx = static_cast<DeclContext *>(S->getEntity()); + while (Ctx && isa<LinkageSpecDecl>(Ctx)) { + if (cast<LinkageSpecDecl>(Ctx)->getLanguage() != LinkageSpecDecl::lang_cxx) + return Diag(TemplateLoc, diag::err_template_linkage) + << TemplateRange; + + Ctx = Ctx->getParent(); + } + + if (Ctx && (Ctx->isFileContext() || Ctx->isRecord())) + return false; + + return Diag(TemplateLoc, diag::err_template_outside_namespace_or_class_scope) + << TemplateRange; +} |