From 7caa6825f42a0f7e97d6fc06233133c42b218e46 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 24 Jul 2009 20:34:43 +0000 Subject: [PATCH] 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 --- include/clang/AST/ASTContext.h | 35 +++++ include/clang/AST/Decl.h | 8 + include/clang/Basic/DiagnosticSemaKinds.td | 2 + lib/AST/ASTContext.cpp | 19 +++ lib/AST/Decl.cpp | 4 + lib/Sema/Sema.h | 5 +- lib/Sema/SemaExpr.cpp | 13 +- lib/Sema/SemaTemplateInstantiate.cpp | 13 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 142 +++++++++++++++++- .../temp.class/temp.static/p1-inst.cpp | 28 ++++ .../temp.decls/temp.class/temp.static/p1.cpp | 4 +- 11 files changed, 255 insertions(+), 18 deletions(-) create mode 100644 test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 6af58288bb..78c6122655 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -136,6 +136,32 @@ class ASTContext { /// wasting space in the Decl class. llvm::DenseMap 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 + /// struct X { + /// static T value; + /// }; + /// + /// template + /// T X::value = T(17); + /// + /// int *x = &X::value; + /// \endcode + /// + /// This mapping will contain an entry that maps from the VarDecl for + /// X::value to the corresponding VarDecl for X::value (within the + /// class template X). + llvm::DenseMap 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); diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d99873823c..f8ee70b752 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -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(Ctx) || isa(Ctx) ) return true; } + if (isStaticDataMember() && isOutOfLine()) + return true; + return false; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9031a6ec9c..9540091a82 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 5572b7a3a0..4684a806f0 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -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::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 { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b4710c2c53..9701f6c90d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3f14c49ed4..164c65f02c 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 66e73f99e9..98d1f4c6af 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5780,10 +5780,17 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { } if (VarDecl *Var = dyn_cast(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 a75a8584d1..cb43f1c6a0 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -189,8 +189,7 @@ void Sema::PrintInstantiationStack() { DiagID) << Context.getTypeDeclType(Record) << Active->InstantiationRange; - } else { - FunctionDecl *Function = cast(D); + } else if (FunctionDecl *Function = dyn_cast(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(D) + << Active->InstantiationRange; } break; } @@ -1059,9 +1063,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, if (!Function->getBody()) InstantiateFunctionDefinition(PointOfInstantiation, Function); } else if (VarDecl *Var = dyn_cast(*D)) { - const VarDecl *Def = 0; - if (!Var->getDefinition(Def)) - InstantiateVariableDefinition(Var); + if (Var->isStaticDataMember()) + InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var); } else if (CXXRecordDecl *Record = dyn_cast(*D)) { if (!Record->isInjectedClassName() && !Record->getDefinition(Context)) { assert(Record->getInstantiatedFromMemberClass() && diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5718e9b69b..a6513f1672 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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 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(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(Other)) + if (Var->isStaticDataMember()) + return Var->getInstantiatedFromStaticDataMember()->getCanonicalDecl() + == D->getCanonicalDecl(); + // FIXME: How can we find instantiations of anonymous unions? return D->getDeclName() && isa(Other) && @@ -912,10 +1034,16 @@ void Sema::PerformPendingImplicitInstantiations() { PendingImplicitInstantiation Inst = PendingImplicitInstantiations.front(); PendingImplicitInstantiations.pop_front(); - if (FunctionDecl *Function = dyn_cast(Inst.first)) + // Instantiate function definitions + if (FunctionDecl *Function = dyn_cast(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(Inst.first); + assert(Var->isStaticDataMember() && "Not a static data member?"); + InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true); } } diff --git a/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp b/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp new file mode 100644 index 0000000000..b13ca93764 --- /dev/null +++ b/test/CXX/temp/temp.decls/temp.class/temp.static/p1-inst.cpp @@ -0,0 +1,28 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// Test instantiation of static data members declared out-of-line. + +template +struct X { + static T value; +}; + +template + T X::value = 17; // expected-error{{initialize}} + +struct InitOkay { + InitOkay(int) { } +}; + +struct CannotInit { }; + +int &returnInt() { return X::value; } +float &returnFloat() { return X::value; } + +InitOkay &returnInitOkay() { return X::value; } + +unsigned long sizeOkay() { return sizeof(X::value); } + +CannotInit &returnError() { + return X::value; // expected-note{{instantiation}} +} \ No newline at end of file diff --git a/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp b/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp index 41d5f1baae..37cae38cac 100644 --- a/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.class/temp.static/p1.cpp @@ -6,7 +6,7 @@ struct X0 { }; template -T X0::value = 0; +T X0::value = 0; // expected-error{{initialize}} struct X1 { X1(int); @@ -20,7 +20,7 @@ X1& get_X1() { return X0::value; } double*& get_double_ptr() { return X0::value; } // expected-error{{initialized}} X2& get_X2() { - return X0::value; // FIXME: instantiation should fail! + return X0::value; // expected-note{{instantiation}} } template T x; // expected-error{{variable 'x' declared as a template}} \ No newline at end of file -- 2.40.0