[cfe-commits] r77002 - in /cfe/trunk: include/clang/AST/ASTContext.h include/clang/AST/Decl.h include/clang/Basic/DiagnosticSemaKinds.td lib/AST/ASTContext.cpp lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaExpr.cpp lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp
Douglas Gregor
dgregor at apple.com
Fri Jul 24 13:34:43 PDT 2009
Author: dgregor
Date: Fri Jul 24 15:34:43 2009
New Revision: 77002
URL: http://llvm.org/viewvc/llvm-project?rev=77002&view=rev
Log:
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.
Added:
cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp
Modified:
cfe/trunk/include/clang/AST/ASTContext.h
cfe/trunk/include/clang/AST/Decl.h
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/AST/ASTContext.cpp
cfe/trunk/lib/AST/Decl.cpp
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaExpr.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp
Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Fri Jul 24 15:34:43 2009
@@ -136,6 +136,32 @@
/// 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 @@
/// \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);
Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Fri Jul 24 15:34:43 2009
@@ -477,6 +477,11 @@
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 @@
if (isa<TranslationUnitDecl>(Ctx) || isa<NamespaceDecl>(Ctx) )
return true;
}
+ if (isStaticDataMember() && isOutOfLine())
+ return true;
+
return false;
}
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jul 24 15:34:43 2009
@@ -888,6 +888,8 @@
"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">;
Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jul 24 15:34:43 2009
@@ -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> {
Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Fri Jul 24 15:34:43 2009
@@ -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;
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri Jul 24 15:34:43 2009
@@ -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);
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Jul 24 15:34:43 2009
@@ -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;
+}
}
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Jul 24 15:34:43 2009
@@ -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() &&
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Fri Jul 24 15:34:43 2009
@@ -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);
}
}
Added: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp?rev=77002&view=auto
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp Fri Jul 24 15:34:43 2009
@@ -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}}
+}
\ No newline at end of file
Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp?rev=77002&r1=77001&r2=77002&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp Fri Jul 24 15:34:43 2009
@@ -6,7 +6,7 @@
};
template<typename T>
-T X0<T>::value = 0;
+T X0<T>::value = 0; // expected-error{{initialize}}
struct X1 {
X1(int);
@@ -20,7 +20,7 @@
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}}
\ No newline at end of file
More information about the cfe-commits
mailing list