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
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 7d36291..33b3781 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -61,6 +61,7 @@
class ExtVectorType;
class TypedefDecl;
class TemplateDecl;
+ class TemplateParameterList;
class ObjCInterfaceDecl;
class ObjCCompatibleAliasDecl;
class ObjCProtocolDecl;
@@ -335,8 +336,7 @@
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 @@
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 22b8ef2..7ccc13c 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -707,7 +707,8 @@
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 @@
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 @@
/// 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 @@
DeclContext *SearchDC = CurContext;
DeclContext *DC = CurContext;
NamedDecl *PrevDecl = 0;
- TemplateDecl *PrevTemplate = 0;
bool Invalid = false;
@@ -2892,11 +2879,6 @@
}
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 @@
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 @@
// 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 @@
// 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 e15343a..6c5f90a 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 @@
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 @@
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;
+}