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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@77002 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 5572b7a..4684a80 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -220,6 +220,25 @@
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> {
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index b4710c2..9701f6c 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -341,6 +341,10 @@
return SourceRange(getLocation(), getLocation());
}
+VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
+ return getASTContext().getInstantiatedFromStaticDataMember(this);
+}
+
bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
return false;
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 3f14c49..164c65f 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2732,7 +2732,10 @@
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);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 66e73f9..98d1f4c 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -5780,10 +5780,17 @@
}
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;
+}
}
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index a75a858..cb43f1c 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -189,8 +189,7 @@
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 @@
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 @@
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() &&
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5718e9b..a6513f1 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -120,12 +120,24 @@
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 @@
// 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 @@
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 @@
/// \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 @@
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 @@
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);
}
}