Template instantiation for static data members that are defined out-of-line.

Note that this also fixes a bug that affects non-template code, where we 
were not treating out-of-line static data members are "file-scope" variables,
and therefore not checking their initializers.

llvm-svn: 77002
This commit is contained in:
Douglas Gregor 2009-07-24 20:34:43 +00:00
parent 4dcc53f312
commit a6ef8f0813
11 changed files with 255 additions and 18 deletions

View File

@ -136,6 +136,32 @@ class ASTContext {
/// wasting space in the Decl class.
llvm::DenseMap<const Decl*, Attr*> DeclAttrs;
/// \brief Keeps track of the static data member templates from which
/// static data members of class template specializations were instantiated.
///
/// 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.
///
/// Given the following example:
///
/// \code
/// template<typename T>
/// struct X {
/// static T value;
/// };
///
/// template<typename T>
/// T X<T>::value = T(17);
///
/// int *x = &X<int>::value;
/// \endcode
///
/// 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;
TranslationUnitDecl *TUDecl;
/// SourceMgr - The associated SourceManager object.
@ -193,6 +219,15 @@ public:
/// \brief Erase the attributes corresponding to the given declaration.
void eraseDeclAttrs(const Decl *D) { DeclAttrs.erase(D); }
/// \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);
/// \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);
TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
const char *getCommentForDecl(const Decl *D);

View File

@ -477,6 +477,11 @@ public:
return getDeclContext()->isRecord();
}
/// \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();
/// isFileVarDecl - Returns true for file scoped variable declaration.
bool isFileVarDecl() const {
if (getKind() != Decl::Var)
@ -486,6 +491,9 @@ public:
if (isa<TranslationUnitDecl>(Ctx) || isa<NamespaceDecl>(Ctx) )
return true;
}
if (isStaticDataMember() && isOutOfLine())
return true;
return false;
}

View File

@ -888,6 +888,8 @@ def note_template_member_function_here : Note<
"in instantiation of member function %q0 requested here">;
def note_function_template_spec_here : Note<
"in instantiation of function template specialization %q0 requested here">;
def note_template_static_data_member_def_here : Note<
"in instantiation of static data member %q0 requested here">;
def note_default_arg_instantiation_here : Note<
"in instantiation of default argument for '%0' required here">;

View File

@ -220,6 +220,25 @@ void ASTContext::InitBuiltinTypes() {
InitBuiltinType(NullPtrTy, BuiltinType::NullPtr);
}
VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
assert(Var->isStaticDataMember() && "Not a static data member");
llvm::DenseMap<VarDecl *, VarDecl *>::iterator Pos
= InstantiatedFromStaticDataMember.find(Var);
if (Pos == InstantiatedFromStaticDataMember.end())
return 0;
return Pos->second;
}
void
ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) {
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;
}
namespace {
class BeforeInTranslationUnit
: std::binary_function<SourceRange, SourceRange, bool> {

View File

@ -341,6 +341,10 @@ SourceRange VarDecl::getSourceRange() const {
return SourceRange(getLocation(), getLocation());
}
VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
return getASTContext().getInstantiatedFromStaticDataMember(this);
}
bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
return false;

View File

@ -2732,7 +2732,10 @@ public:
void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
FunctionDecl *Function,
bool Recursive = false);
void InstantiateVariableDefinition(VarDecl *Var);
void InstantiateStaticDataMemberDefinition(
SourceLocation PointOfInstantiation,
VarDecl *Var,
bool Recursive = false);
NamedDecl *InstantiateCurrentDeclRef(NamedDecl *D);

View File

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

View File

@ -189,8 +189,7 @@ void Sema::PrintInstantiationStack() {
DiagID)
<< Context.getTypeDeclType(Record)
<< Active->InstantiationRange;
} else {
FunctionDecl *Function = cast<FunctionDecl>(D);
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
unsigned DiagID;
if (Function->getPrimaryTemplate())
DiagID = diag::note_function_template_spec_here;
@ -200,6 +199,11 @@ void Sema::PrintInstantiationStack() {
DiagID)
<< Function
<< Active->InstantiationRange;
} else {
Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
diag::note_template_static_data_member_def_here)
<< cast<VarDecl>(D)
<< Active->InstantiationRange;
}
break;
}
@ -1059,9 +1063,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
if (!Function->getBody())
InstantiateFunctionDefinition(PointOfInstantiation, Function);
} else if (VarDecl *Var = dyn_cast<VarDecl>(*D)) {
const VarDecl *Def = 0;
if (!Var->getDefinition(Def))
InstantiateVariableDefinition(Var);
if (Var->isStaticDataMember())
InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
} else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(*D)) {
if (!Record->isInjectedClassName() && !Record->getDefinition(Context)) {
assert(Record->getInstantiatedFromMemberClass() &&

View File

@ -120,12 +120,24 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
Var->setCXXDirectInitializer(D->hasCXXDirectInitializer());
Var->setDeclaredInCondition(D->isDeclaredInCondition());
// If we are instantiating a static data member defined
// out-of-line, the instantiation will have the same lexical
// context (which will be a namespace scope) as the template.
if (D->isOutOfLine())
Var->setLexicalDeclContext(D->getLexicalDeclContext());
// FIXME: In theory, we could have a previous declaration for variables that
// are not static data members.
bool Redeclaration = false;
SemaRef.CheckVariableDeclaration(Var, 0, Redeclaration);
Owner->addDecl(Var);
if (D->isOutOfLine()) {
D->getLexicalDeclContext()->addDecl(Var);
Owner->makeDeclVisibleInContext(Var);
} else {
Owner->addDecl(Var);
}
if (D->getInit()) {
OwningExprResult Init
= SemaRef.InstantiateExpr(D->getInit(), TemplateArgs);
@ -138,6 +150,11 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
// FIXME: Call ActOnUninitializedDecl? (Not always)
}
// 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;
}
@ -374,6 +391,12 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
D->isInline());
Method->setInstantiationOfMemberFunction(D);
// If we are instantiating a member function defined
// out-of-line, the instantiation will have the same lexical
// context (which will be a namespace scope) as the template.
if (D->isOutOfLine())
Method->setLexicalDeclContext(D->getLexicalDeclContext());
// Attach the parameters
for (unsigned P = 0; P < Params.size(); ++P)
Params[P]->setOwningFunction(Method);
@ -773,9 +796,103 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
/// \brief Instantiate the definition of the given variable from its
/// template.
///
/// \param Var the already-instantiated declaration of a variable.
void Sema::InstantiateVariableDefinition(VarDecl *Var) {
// FIXME: Implement this!
/// \param PointOfInstantiation the point at which the instantiation was
/// required. Note that this is not precisely a "point of instantiation"
/// for the function, but it's close.
///
/// \param Var the already-instantiated declaration of a static member
/// variable of a class template specialization.
///
/// \param Recursive if true, recursively instantiates any functions that
/// are required by this instantiation.
void Sema::InstantiateStaticDataMemberDefinition(
SourceLocation PointOfInstantiation,
VarDecl *Var,
bool Recursive) {
if (Var->isInvalidDecl())
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?");
assert(Def->isStaticDataMember() && "Not a static data member?");
for (VarDecl::redecl_iterator RD = Def->redecls_begin(),
RDEnd = Def->redecls_end();
RD != RDEnd; ++RD) {
if (RD->getLexicalDeclContext()->isFileContext()) {
Def = *RD;
FoundOutOfLineDef = true;
}
}
if (!FoundOutOfLineDef) {
// We did not find an out-of-line definition of this static data member,
// so we won't perform any instantiation. Rather, we rely on the user to
// instantiate this definition (or provide a specialization for it) in
// another translation unit.
return;
}
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
if (Inst)
return;
// If we're performing recursive template instantiation, create our own
// queue of pending implicit instantiations that we will instantiate later,
// while we're still within our own instantiation context.
std::deque<PendingImplicitInstantiation> SavedPendingImplicitInstantiations;
if (Recursive)
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
DeclContext *PreviousContext = CurContext;
CurContext = Var->getDeclContext();
#if 0
// Instantiate the initializer of this static data member.
OwningExprResult Init
= InstantiateExpr(Def->getInit(), getTemplateInstantiationArgs(Var));
if (Init.isInvalid()) {
// If instantiation of the initializer failed, mark the declaration invalid
// and don't instantiate anything else that was triggered by this
// instantiation.
Var->setInvalidDecl();
// Restore the set of pending implicit instantiations.
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
return;
}
// Type-check the initializer.
if (Init.get())
AddInitializerToDecl(DeclPtrTy::make(Var), move(Init),
Def->hasCXXDirectInitializer());
else
ActOnUninitializedDecl(DeclPtrTy::make(Var), false);
#else
Var = cast_or_null<VarDecl>(InstantiateDecl(Def, Var->getDeclContext(),
getTemplateInstantiationArgs(Var)));
#endif
CurContext = PreviousContext;
if (Var) {
DeclGroupRef DG(Var);
Consumer.HandleTopLevelDecl(DG);
}
if (Recursive) {
// Instantiate any pending implicit instantiations found during the
// instantiation of this template.
PerformPendingImplicitInstantiations();
// Restore the set of pending implicit instantiations.
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
}
}
static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
@ -794,6 +911,11 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
return Enum->getInstantiatedFromMemberEnum()->getCanonicalDecl()
== D->getCanonicalDecl();
if (VarDecl *Var = dyn_cast<VarDecl>(Other))
if (Var->isStaticDataMember())
return Var->getInstantiatedFromStaticDataMember()->getCanonicalDecl()
== D->getCanonicalDecl();
// FIXME: How can we find instantiations of anonymous unions?
return D->getDeclName() && isa<NamedDecl>(Other) &&
@ -912,10 +1034,16 @@ void Sema::PerformPendingImplicitInstantiations() {
PendingImplicitInstantiation Inst = PendingImplicitInstantiations.front();
PendingImplicitInstantiations.pop_front();
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first))
// Instantiate function definitions
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first)) {
if (!Function->getBody())
InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true);
continue;
}
// FIXME: instantiate static member variables
// Instantiate static data member definitions.
VarDecl *Var = cast<VarDecl>(Inst.first);
assert(Var->isStaticDataMember() && "Not a static data member?");
InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true);
}
}

View File

@ -0,0 +1,28 @@
// RUN: clang-cc -fsyntax-only -verify %s
// Test instantiation of static data members declared out-of-line.
template<typename T>
struct X {
static T value;
};
template<typename T>
T X<T>::value = 17; // expected-error{{initialize}}
struct InitOkay {
InitOkay(int) { }
};
struct CannotInit { };
int &returnInt() { return X<int>::value; }
float &returnFloat() { return X<float>::value; }
InitOkay &returnInitOkay() { return X<InitOkay>::value; }
unsigned long sizeOkay() { return sizeof(X<CannotInit>::value); }
CannotInit &returnError() {
return X<CannotInit>::value; // expected-note{{instantiation}}
}

View File

@ -6,7 +6,7 @@ struct X0 {
};
template<typename T>
T X0<T>::value = 0;
T X0<T>::value = 0; // expected-error{{initialize}}
struct X1 {
X1(int);
@ -20,7 +20,7 @@ X1& get_X1() { return X0<X1>::value; }
double*& get_double_ptr() { return X0<int*>::value; } // expected-error{{initialized}}
X2& get_X2() {
return X0<X2>::value; // FIXME: instantiation should fail!
return X0<X2>::value; // expected-note{{instantiation}}
}
template<typename T> T x; // expected-error{{variable 'x' declared as a template}}