diff options
Diffstat (limited to 'lib/Sema')
-rw-r--r-- | lib/Sema/Sema.h | 18 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 47 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 269 |
3 files changed, 288 insertions, 46 deletions
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 7d36291dd3..33b3781200 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -61,6 +61,7 @@ namespace clang { class ExtVectorType; class TypedefDecl; class TemplateDecl; + class TemplateParameterList; class ObjCInterfaceDecl; class ObjCCompatibleAliasDecl; class ObjCProtocolDecl; @@ -335,8 +336,7 @@ public: virtual DeclTy *ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, - AttributeList *Attr, - MultiTemplateParamsArg TemplateParameterLists); + AttributeList *Attr); virtual void ActOnDefs(Scope *S, DeclTy *TagD, SourceLocation DeclStart, IdentifierInfo *ClassName, @@ -1505,6 +1505,20 @@ public: DeclTy **Params, unsigned NumParams, SourceLocation RAngleLoc); + virtual DeclTy * + ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, + SourceLocation KWLoc, const CXXScopeSpec &SS, + IdentifierInfo *Name, SourceLocation NameLoc, + AttributeList *Attr, + MultiTemplateParamsArg TemplateParameterLists); + + bool TemplateParameterListsAreEqual(TemplateParameterList *New, + TemplateParameterList *Old, + bool Complain, + bool IsTemplateTemplateParm = false); + + bool CheckTemplateDeclScope(Scope *S, + MultiTemplateParamsArg &TemplateParameterLists); // Objective-C declarations. virtual DeclTy *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 22b8ef2b1f..7ccc13c97d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -707,7 +707,8 @@ Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { return Tag; } - if (!DS.isMissingDeclaratorOk()) { + if (!DS.isMissingDeclaratorOk() && + DS.getTypeSpecType() != DeclSpec::TST_error) { // Warn about typedefs of enums without names, since this is an // extension in both Microsoft an GNU. if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef && @@ -717,10 +718,6 @@ Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { return Tag; } - // FIXME: This diagnostic is emitted even when various previous - // errors occurred (see e.g. test/Sema/decl-invalid.c). However, - // DeclSpec has no means of communicating this information, and the - // responsible parser functions are quite far apart. Diag(DS.getSourceRange().getBegin(), diag::err_no_declarators) << DS.getSourceRange(); return 0; @@ -2798,22 +2795,13 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TK indicates whether this is a /// reference/declaration/definition of a tag. -/// -/// This creates and returns template declarations if any template parameter -/// lists are given. Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, - AttributeList *Attr, - MultiTemplateParamsArg TemplateParameterLists) { + AttributeList *Attr) { // If this is not a definition, it must have a name. assert((Name != 0 || TK == TK_Definition) && "Nameless record must be a definition!"); - assert((TemplateParameterLists.size() == 0 || TK != TK_Reference) && - "Can't have a reference to a template"); - assert((TemplateParameterLists.size() == 0 || - TagSpec != DeclSpec::TST_enum) && - "No such thing as an enum template"); TagDecl::TagKind Kind; switch (TagSpec) { @@ -2827,7 +2815,6 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, DeclContext *SearchDC = CurContext; DeclContext *DC = CurContext; NamedDecl *PrevDecl = 0; - TemplateDecl *PrevTemplate = 0; bool Invalid = false; @@ -2892,11 +2879,6 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, } if (PrevDecl) { - // If we found a template, keep track of the template and its - // underlying declaration. - if ((PrevTemplate = dyn_cast_or_null<ClassTemplateDecl>(PrevDecl))) - PrevDecl = PrevTemplate->getTemplatedDecl(); - if (TagDecl *PrevTagDecl = dyn_cast<TagDecl>(PrevDecl)) { // If this is a use of a previous tag, or if the tag is already declared // in the same scope (so that the definition/declaration completes or @@ -2911,7 +2893,6 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, Name = 0; PrevDecl = 0; Invalid = true; - // FIXME: Add template/non-template redecl check } else { // If this is a use, just return the declaration we found. @@ -3025,7 +3006,6 @@ CreateNewDecl: // declaration of the same entity, the two will be linked via // PrevDecl. TagDecl *New; - ClassTemplateDecl *NewTemplate = 0; if (Kind == TagDecl::TK_enum) { // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.: @@ -3039,28 +3019,11 @@ CreateNewDecl: // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.: // struct X { int A; } D; D should chain to X. - if (getLangOptions().CPlusPlus) { + if (getLangOptions().CPlusPlus) // FIXME: Look for a way to use RecordDecl for simple structs. New = CXXRecordDecl::Create(Context, Kind, SearchDC, Loc, Name, cast_or_null<CXXRecordDecl>(PrevDecl)); - - // If there's are template parameters, then this must be a class - // template. Create the template decl node also. - // FIXME: Do we always create template decls? We may not for forward - // declarations. - // FIXME: What are we actually going to do with the template decl? - if (TemplateParameterLists.size() > 0) { - // FIXME: The allocation of the parameters is probably incorrect. - // FIXME: Does the TemplateDecl have the same name as the class? - TemplateParameterList *Params = - TemplateParameterList::Create(Context, - (Decl **)TemplateParameterLists.get(), - TemplateParameterLists.size()); - NewTemplate = ClassTemplateDecl::Create(Context, DC, Loc, - DeclarationName(Name), Params, - New); - } - } else + else New = RecordDecl::Create(Context, Kind, SearchDC, Loc, Name, cast_or_null<RecordDecl>(PrevDecl)); } 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; +} |