For instantiations of static data members of class templates, keep

track of the kind of specialization or instantiation. Also, check the
scope of the specialization and ensure that a specialization
declaration without an initializer is not a definition.

llvm-svn: 83533
This commit is contained in:
Douglas Gregor 2009-10-08 07:24:58 +00:00
parent 662efc9185
commit 86d142a801
12 changed files with 194 additions and 92 deletions

View File

@ -154,7 +154,8 @@ class ASTContext {
///
/// This data structure stores the mapping from instantiations of static
/// data members to the static data member representations within the
/// class template from which they were instantiated.
/// class template from which they were instantiated along with the kind
/// of instantiation or specialization (a TemplateSpecializationKind - 1).
///
/// Given the following example:
///
@ -172,8 +173,9 @@ class ASTContext {
///
/// This mapping will contain an entry that maps from the VarDecl for
/// X<int>::value to the corresponding VarDecl for X<T>::value (within the
/// class template X).
llvm::DenseMap<VarDecl *, VarDecl *> InstantiatedFromStaticDataMember;
/// class template X) and will be marked TSK_ImplicitInstantiation.
llvm::DenseMap<VarDecl *, MemberSpecializationInfo *>
InstantiatedFromStaticDataMember;
/// \brief Keeps track of the UnresolvedUsingDecls from which UsingDecls
/// where created during instantiation.
@ -265,11 +267,12 @@ public:
/// \brief If this variable is an instantiated static data member of a
/// class template specialization, returns the templated static data member
/// from which it was instantiated.
VarDecl *getInstantiatedFromStaticDataMember(VarDecl *Var);
MemberSpecializationInfo *getInstantiatedFromStaticDataMember(VarDecl *Var);
/// \brief Note that the static data member \p Inst is an instantiation of
/// the static data member template \p Tmpl of a class template.
void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl);
void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
TemplateSpecializationKind TSK);
/// \brief If this using decl is instantiated from an unresolved using decl,
/// return it.

View File

@ -300,6 +300,29 @@ struct EvaluatedStmt {
APValue Evaluated;
};
// \brief Describes the kind of template specialization that a
// particular template specialization declaration represents.
enum TemplateSpecializationKind {
/// This template specialization was formed from a template-id but
/// has not yet been declared, defined, or instantiated.
TSK_Undeclared = 0,
/// This template specialization was implicitly instantiated from a
/// template. (C++ [temp.inst]).
TSK_ImplicitInstantiation,
/// This template specialization was declared or defined by an
/// explicit specialization (C++ [temp.expl.spec]) or partial
/// specialization (C++ [temp.class.spec]).
TSK_ExplicitSpecialization,
/// This template specialization was instantiated from a template
/// due to an explicit instantiation declaration request
/// (C++0x [temp.explicit]).
TSK_ExplicitInstantiationDeclaration,
/// This template specialization was instantiated from a template
/// due to an explicit instantiation definition request
/// (C++ [temp.explicit]).
TSK_ExplicitInstantiationDefinition
};
/// VarDecl - An instance of this class is created to represent a variable
/// declaration or definition.
class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
@ -566,6 +589,14 @@ public:
/// from which it was instantiated.
VarDecl *getInstantiatedFromStaticDataMember();
/// \brief If this variable is a static data member, determine what kind of
/// template specialization or instantiation this is.
TemplateSpecializationKind getTemplateSpecializationKind();
/// \brief For a static data member that was instantiated from a static
/// data member of a class template, set the template specialiation kind.
void setTemplateSpecializationKind(TemplateSpecializationKind TSK);
/// isFileVarDecl - Returns true for file scoped variable declaration.
bool isFileVarDecl() const {
if (getKind() != Decl::Var)
@ -754,29 +785,6 @@ public:
static bool classof(const OriginalParmVarDecl *D) { return true; }
};
// \brief Describes the kind of template specialization that a
// particular template specialization declaration represents.
enum TemplateSpecializationKind {
/// This template specialization was formed from a template-id but
/// has not yet been declared, defined, or instantiated.
TSK_Undeclared = 0,
/// This template specialization was implicitly instantiated from a
/// template. (C++ [temp.inst]).
TSK_ImplicitInstantiation,
/// This template specialization was declared or defined by an
/// explicit specialization (C++ [temp.expl.spec]) or partial
/// specialization (C++ [temp.class.spec]).
TSK_ExplicitSpecialization,
/// This template specialization was instantiated from a template
/// due to an explicit instantiation declaration request
/// (C++0x [temp.explicit]).
TSK_ExplicitInstantiationDeclaration,
/// This template specialization was instantiated from a template
/// due to an explicit instantiation definition request
/// (C++ [temp.explicit]).
TSK_ExplicitInstantiationDefinition
};
/// FunctionDecl - An instance of this class is created to represent a
/// function declaration or definition.
///

View File

@ -948,9 +948,8 @@ 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">;
def err_function_spec_not_instantiated : Error<
"specialization of member function %q0 does not specialize an instantiated "
"member function">;
def err_spec_member_not_instantiated : Error<
"specialization of member %q0 does not specialize an instantiated member">;
def note_specialized_decl : Note<"attempt to specialize declaration here">;
// C++ class template specializations and out-of-line definitions

View File

@ -232,9 +232,10 @@ void ASTContext::InitBuiltinTypes() {
InitBuiltinType(NullPtrTy, BuiltinType::NullPtr);
}
VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
MemberSpecializationInfo *
ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
assert(Var->isStaticDataMember() && "Not a static data member");
llvm::DenseMap<VarDecl *, VarDecl *>::iterator Pos
llvm::DenseMap<VarDecl *, MemberSpecializationInfo *>::iterator Pos
= InstantiatedFromStaticDataMember.find(Var);
if (Pos == InstantiatedFromStaticDataMember.end())
return 0;
@ -243,12 +244,14 @@ VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
}
void
ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) {
ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
TemplateSpecializationKind TSK) {
assert(Inst->isStaticDataMember() && "Not a static data member");
assert(Tmpl->isStaticDataMember() && "Not a static data member");
assert(!InstantiatedFromStaticDataMember[Inst] &&
"Already noted what static data member was instantiated from");
InstantiatedFromStaticDataMember[Inst] = Tmpl;
InstantiatedFromStaticDataMember[Inst]
= new (*this) MemberSpecializationInfo(Tmpl, TSK);
}
UnresolvedUsingDecl *

View File

@ -371,7 +371,26 @@ SourceRange VarDecl::getSourceRange() const {
}
VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
return getASTContext().getInstantiatedFromStaticDataMember(this);
if (MemberSpecializationInfo *MSI
= getASTContext().getInstantiatedFromStaticDataMember(this))
return cast<VarDecl>(MSI->getInstantiatedFrom());
return 0;
}
TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() {
if (MemberSpecializationInfo *MSI
= getASTContext().getInstantiatedFromStaticDataMember(this))
return MSI->getTemplateSpecializationKind();
return TSK_Undeclared;
}
void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
MemberSpecializationInfo *MSI
= getASTContext().getInstantiatedFromStaticDataMember(this);
assert(MSI && "Not an instantiated static data member?");
MSI->setTemplateSpecializationKind(TSK);
}
bool VarDecl::isTentativeDefinition(ASTContext &Context) const {

View File

@ -2519,8 +2519,7 @@ public:
unsigned NumExplicitTemplateArgs,
SourceLocation RAngleLoc,
NamedDecl *&PrevDecl);
bool CheckMemberFunctionSpecialization(CXXMethodDecl *FD,
NamedDecl *&PrevDecl);
bool CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl);
virtual DeclResult
ActOnExplicitInstantiation(Scope *S,

View File

@ -2193,7 +2193,6 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// Match up the template parameter lists with the scope specifier, then
// determine whether we have a template or a template specialization.
// FIXME: Actually record when this is an explicit specialization!
bool isExplicitSpecialization = false;
if (TemplateParameterList *TemplateParams
= MatchTemplateParametersToScopeSpecifier(
@ -2259,7 +2258,7 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
!(NewVD->hasLinkage() &&
isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
PrevDecl = 0;
// Merge the decl with the existing one if appropriate.
if (PrevDecl) {
if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) {
@ -2281,6 +2280,11 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
CheckVariableDeclaration(NewVD, PrevDecl, Redeclaration);
// This is an explicit specialization of a static data member. Check it.
if (isExplicitSpecialization && !NewVD->isInvalidDecl() &&
CheckMemberSpecialization(NewVD, PrevDecl))
NewVD->setInvalidDecl();
// attributes declared post-definition are currently ignored
if (PrevDecl) {
const VarDecl *Def = 0, *PrevVD = dyn_cast<VarDecl>(PrevDecl);
@ -2837,8 +2841,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
PrevDecl))
NewFD->setInvalidDecl();
} else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD) &&
CheckMemberFunctionSpecialization(cast<CXXMethodDecl>(NewFD),
PrevDecl))
CheckMemberSpecialization(NewFD, PrevDecl))
NewFD->setInvalidDecl();
// Perform semantic checking on the function declaration.
@ -3400,6 +3403,15 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
return;
}
// C++ [temp.expl.spec]p15:
// An explicit specialization of a static data member of a template is a
// definition if the declaration includes an initializer; otherwise, it
// is a declaration.
if (Var->isStaticDataMember() &&
Var->getInstantiatedFromStaticDataMember() &&
Var->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
return;
// C++ [dcl.init]p9:
// If no initializer is specified for an object, and the object
// is of (possibly cv-qualified) non-POD class type (or array

View File

@ -6209,11 +6209,9 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
// Implicit instantiation of static data members of class templates.
// FIXME: distinguish between implicit instantiations (which we need to
// actually instantiate) and explicit specializations.
// FIXME: extern templates
if (Var->isStaticDataMember() &&
Var->getInstantiatedFromStaticDataMember())
Var->getInstantiatedFromStaticDataMember() &&
Var->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
// FIXME: keep track of references to static data?

View File

@ -2365,8 +2365,9 @@ static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) {
return CTS->getSpecializationKind();
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
return Function->getTemplateSpecializationKind();
// FIXME: static data members!
if (VarDecl *Var = dyn_cast<VarDecl>(D))
return Var->getTemplateSpecializationKind();
// FIXME: member classes of class templates!
return TSK_Undeclared;
}
@ -3118,7 +3119,7 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
return false;
}
/// \brief Perform semantic analysis for the given member function
/// \brief Perform semantic analysis for the given non-template member
/// specialization.
///
/// This routine performs all of the semantic analysis required for an
@ -3126,27 +3127,45 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
/// the function declaration \p FD will become a member function
/// specialization.
///
/// \param FD the function declaration, which will be updated to become a
/// function template specialization.
/// \param Member the member declaration, which will be updated to become a
/// specialization.
///
/// \param PrevDecl the set of declarations, one of which may be specialized
/// by this function specialization.
bool
Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD,
NamedDecl *&PrevDecl) {
// Try to find the member function we are instantiating.
CXXMethodDecl *Instantiation = 0;
for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*Ovl)) {
if (Context.hasSameType(FD->getType(), Method->getType())) {
Instantiation = Method;
break;
Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
assert(!isa<TemplateDecl>(Member) && "Only for non-template members");
// Try to find the member we are instantiating.
NamedDecl *Instantiation = 0;
NamedDecl *InstantiatedFrom = 0;
if (!PrevDecl) {
// Nowhere to look anyway.
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) {
for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*Ovl)) {
if (Context.hasSameType(Function->getType(), Method->getType())) {
Instantiation = Method;
InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
break;
}
}
}
} else if (isa<VarDecl>(Member)) {
if (VarDecl *PrevVar = dyn_cast<VarDecl>(PrevDecl))
if (PrevVar->isStaticDataMember()) {
Instantiation = PrevDecl;
InstantiatedFrom = PrevVar->getInstantiatedFromStaticDataMember();
}
} else if (isa<RecordDecl>(Member)) {
if (CXXRecordDecl *PrevRecord = dyn_cast<CXXRecordDecl>(PrevDecl)) {
Instantiation = PrevDecl;
InstantiatedFrom = PrevRecord->getInstantiatedFromMemberClass();
}
}
if (!Instantiation) {
// There is no previous declaration that matches. Since member function
// There is no previous declaration that matches. Since member
// specializations are always out-of-line, the caller will complain about
// this mismatch later.
return false;
@ -3155,30 +3174,43 @@ Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD,
// FIXME: Check if the prior declaration has a point of instantiation.
// If so, we have run afoul of C++ [temp.expl.spec]p6.
// Make sure that this is a specialization of a member function.
FunctionDecl *FunctionInTemplate
= Instantiation->getInstantiatedFromMemberFunction();
if (!FunctionInTemplate) {
Diag(FD->getLocation(), diag::err_function_spec_not_instantiated)
<< FD;
// Make sure that this is a specialization of a member.
if (!InstantiatedFrom) {
Diag(Member->getLocation(), diag::err_spec_member_not_instantiated)
<< Member;
Diag(Instantiation->getLocation(), diag::note_specialized_decl);
return true;
}
// Check the scope of this explicit specialization.
if (CheckTemplateSpecializationScope(*this,
FunctionInTemplate,
Instantiation, FD->getLocation(),
InstantiatedFrom,
Instantiation, Member->getLocation(),
false, TSK_ExplicitSpecialization))
return true;
// FIXME: Check for specialization-after-instantiation errors and such.
// Note that this function is an explicit instantiation of a member function.
Instantiation->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
FD->setInstantiationOfMemberFunction(FunctionInTemplate,
TSK_ExplicitSpecialization);
// Note that this is an explicit instantiation of a member.
if (isa<FunctionDecl>(Member)) {
// FIXME: We're also setting the original instantiation we found to be
// an explicit specialization, although I'd rather not have to do this.
cast<FunctionDecl>(Instantiation)->setTemplateSpecializationKind(
TSK_ExplicitSpecialization);
cast<FunctionDecl>(Member)->setInstantiationOfMemberFunction(
cast<CXXMethodDecl>(InstantiatedFrom),
TSK_ExplicitSpecialization);
} else if (isa<VarDecl>(Member)) {
Context.setInstantiatedFromStaticDataMember(cast<VarDecl>(Member),
cast<VarDecl>(InstantiatedFrom),
TSK_ExplicitSpecialization);
} else {
assert(isa<CXXRecordDecl>(Member) && "Only member classes remain");
// FIXME: Record TSK_ExplicitSpecialization.
cast<CXXRecordDecl>(Member)->setInstantiationOfMemberClass(
cast<CXXRecordDecl>(InstantiatedFrom));
}
// Save the caller the trouble of having to figure out which declaration
// this specialization matches.
PrevDecl = Instantiation;
@ -3547,7 +3579,8 @@ Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
}
// Instantiate static data member.
// FIXME: Note that this is an explicit instantiation.
// FIXME: Check for prior specializations and such.
Prev->setTemplateSpecializationKind(TSK);
if (TSK == TSK_ExplicitInstantiationDefinition)
InstantiateStaticDataMemberDefinition(D.getIdentifierLoc(), Prev, false);

View File

@ -982,9 +982,12 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
if (!Function->getBody() && TSK != TSK_ExplicitInstantiationDeclaration)
InstantiateFunctionDefinition(PointOfInstantiation, Function);
} else if (VarDecl *Var = dyn_cast<VarDecl>(*D)) {
if (Var->isStaticDataMember() &&
TSK != TSK_ExplicitInstantiationDeclaration)
InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
if (Var->isStaticDataMember()) {
Var->setTemplateSpecializationKind(TSK);
if (TSK != TSK_ExplicitInstantiationDeclaration)
InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
}
} else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(*D)) {
if (Record->isInjectedClassName())
continue;

View File

@ -155,6 +155,12 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
Owner->addDecl(Var);
}
// Link instantiations of static data members back to the template from
// which they were instantiated.
if (Var->isStaticDataMember())
SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D,
TSK_ImplicitInstantiation);
if (D->getInit()) {
OwningExprResult Init
= SemaRef.SubstExpr(D->getInit(), TemplateArgs);
@ -191,11 +197,6 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
} else if (!Var->isStaticDataMember() || Var->isOutOfLine())
SemaRef.ActOnUninitializedDecl(Sema::DeclPtrTy::make(Var), false);
// Link instantiations of static data members back to the template from
// which they were instantiated.
if (Var->isStaticDataMember())
SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D);
return Var;
}
@ -977,6 +978,10 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
assert(!Function->getBody() && "Already instantiated!");
// Never instantiate an explicit specialization.
if (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
return;
// Find the function body that we'll be substituting.
const FunctionDecl *PatternDecl = 0;
if (FunctionTemplateDecl *Primary = Function->getPrimaryTemplate()) {
@ -1084,7 +1089,6 @@ void Sema::InstantiateStaticDataMemberDefinition(
return;
// Find the out-of-line definition of this static data member.
// FIXME: Do we have to look for specializations separately?
VarDecl *Def = Var->getInstantiatedFromStaticDataMember();
bool FoundOutOfLineDef = false;
assert(Def && "This data member was not instantiated from a template?");
@ -1106,7 +1110,17 @@ void Sema::InstantiateStaticDataMemberDefinition(
return;
}
// FIXME: extern templates
// Never instantiate an explicit specialization.
if (Def->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
return;
// C++0x [temp.explicit]p9:
// Except for inline functions, other explicit instantiation declarations
// have the effect of suppressing the implicit instantiation of the entity
// to which they refer.
if (Def->getTemplateSpecializationKind()
== TSK_ExplicitInstantiationDeclaration)
return;
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
if (Inst)

View File

@ -49,7 +49,7 @@ namespace N0 {
template<typename T>
struct X0 { // expected-note 2{{here}}
static T member;
static T member; // expected-note{{here}}
void f1(T t) { // expected-note{{explicitly specialized declaration is here}}
t = 17;
@ -106,16 +106,27 @@ void test_x0_cvvoid(N0::X0<const volatile void*> x0, const volatile void *cvp) {
x0.f1(cvp); // okay: we've explicitly specialized
}
#if 0
// FIXME: update the remainder of this test to check for scopes properly.
// -- static data member of a class template
template<>
NonDefaultConstructible X0<NonDefaultConstructible>::member = 17;
namespace N0 {
// This actually tests p15; the following is a declaration, not a definition.
template<>
NonDefaultConstructible X0<NonDefaultConstructible>::member;
template<> long X0<long>::member = 17;
NonDefaultConstructible &get_static_member() {
return X0<NonDefaultConstructible>::member;
template<> float X0<float>::member;
}
NonDefaultConstructible &get_static_member() {
return N0::X0<NonDefaultConstructible>::member;
}
template<> int N0::X0<int>::member; // expected-error{{originally}}
template<> float N0::X0<float>::member = 3.14f;
#if 0
// FIXME: update the remainder of this test to check for scopes properly.
// -- member class of a class template
template<>
struct X0<void*>::Inner { };