From: Douglas Gregor Date: Wed, 14 Oct 2009 21:29:40 +0000 (+0000) Subject: Give explicit and implicit instantiations of static data members of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1028c9f0afc1cc5f4951b39b7067fa57c1fea07b;p=clang Give explicit and implicit instantiations of static data members of class templates the proper linkage. Daniel, please look over the CodeGenModule bits. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@84140 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 972e1f6900..f21541c3e7 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -584,10 +584,14 @@ public: return getDeclContext()->isRecord(); } + /// \brief Determine whether this is or was instantiated from an out-of-line + /// definition of a static data member. + bool isOutOfLine() const; + /// \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 *getInstantiatedFromStaticDataMember() const; /// \brief If this variable is a static data member, determine what kind of /// template specialization or instantiation this is. @@ -596,7 +600,7 @@ public: /// \brief If this variable is an instantiation of a static data member of a /// class template specialization, retrieves the member specialization /// information. - MemberSpecializationInfo *getMemberSpecializationInfo(); + MemberSpecializationInfo *getMemberSpecializationInfo() const; /// \brief For a static data member that was instantiated from a static /// data member of a class template, set the template specialiation kind. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a908299bfe..da7959b16f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -373,7 +373,23 @@ SourceRange VarDecl::getSourceRange() const { return SourceRange(getLocation(), getLocation()); } -VarDecl *VarDecl::getInstantiatedFromStaticDataMember() { +bool VarDecl::isOutOfLine() const { + if (!isStaticDataMember()) + return false; + + if (Decl::isOutOfLine()) + return true; + + // If this static data member was instantiated from a static data member of + // a class template, check whether that static data member was defined + // out-of-line. + if (VarDecl *VD = getInstantiatedFromStaticDataMember()) + return VD->isOutOfLine(); + + return false; +} + +VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const { if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) return cast(MSI->getInstantiatedFrom()); @@ -388,7 +404,7 @@ TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() const { return TSK_Undeclared; } -MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() { +MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const { return getASTContext().getInstantiatedFromStaticDataMember(this); } @@ -809,7 +825,6 @@ FunctionDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) { } bool FunctionDecl::isOutOfLine() const { - // FIXME: Should we restrict this to member functions? if (Decl::isOutOfLine()) return true; diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 36ad7f514e..ecf8eb0d27 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -541,7 +541,12 @@ bool CodeGenModule::MayDeferGeneration(const ValueDecl *Global) { } } - return VD->getStorageClass() == VarDecl::Static; + // Static data may be deferred, but out-of-line static data members + // cannot be. + // FIXME: What if the initializer has side effects? + return VD->isInAnonymousNamespace() || + (VD->getStorageClass() == VarDecl::Static && + !(VD->isStaticDataMember() && VD->isOutOfLine())); } void CodeGenModule::EmitGlobal(GlobalDecl GD) { @@ -928,6 +933,37 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { EmitGlobalVarDefinition(D); } +static CodeGenModule::GVALinkage +GetLinkageForVariable(ASTContext &Context, const VarDecl *VD) { + // Everything located semantically within an anonymous namespace is + // always internal. + if (VD->isInAnonymousNamespace()) + return CodeGenModule::GVA_Internal; + + // Handle linkage for static data members. + if (VD->isStaticDataMember()) { + switch (VD->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + case TSK_ExplicitInstantiationDefinition: + return CodeGenModule::GVA_StrongExternal; + + case TSK_ExplicitInstantiationDeclaration: + assert(false && "Variable should not be instantiated"); + // Fall through to treat this like any other instantiation. + + case TSK_ImplicitInstantiation: + return CodeGenModule::GVA_TemplateInstantiation; + } + } + + // Static variables get internal linkage. + if (VD->getStorageClass() == VarDecl::Static) + return CodeGenModule::GVA_Internal; + + return CodeGenModule::GVA_StrongExternal; +} + void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { llvm::Constant *Init = 0; QualType ASTTy = D->getType(); @@ -1021,9 +1057,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { GV->setAlignment(getContext().getDeclAlignInBytes(D)); // Set the llvm linkage type as appropriate. + GVALinkage Linkage = GetLinkageForVariable(getContext(), D); if (D->isInAnonymousNamespace()) GV->setLinkage(llvm::Function::InternalLinkage); - else if (D->getStorageClass() == VarDecl::Static) + else if (Linkage == GVA_Internal) GV->setLinkage(llvm::Function::InternalLinkage); else if (D->hasAttr()) GV->setLinkage(llvm::Function::DLLImportLinkage); @@ -1034,7 +1071,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { GV->setLinkage(llvm::GlobalVariable::WeakODRLinkage); else GV->setLinkage(llvm::GlobalVariable::WeakAnyLinkage); - } else if (!CompileOpts.NoCommon && + } else if (Linkage == GVA_TemplateInstantiation) + GV->setLinkage(llvm::GlobalVariable::WeakAnyLinkage); + else if (!CompileOpts.NoCommon && !D->hasExternalStorage() && !D->getInit() && !D->getAttr()) { GV->setLinkage(llvm::GlobalVariable::CommonLinkage); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 28c0fa6ab9..060cc55983 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1191,14 +1191,14 @@ void Sema::InstantiateStaticDataMemberDefinition( } // Never instantiate an explicit specialization. - if (Def->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + if (Var->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() + if (Var->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDeclaration) return; @@ -1218,12 +1218,14 @@ void Sema::InstantiateStaticDataMemberDefinition( DeclContext *PreviousContext = CurContext; CurContext = Var->getDeclContext(); + VarDecl *OldVar = Var; Var = cast_or_null(SubstDecl(Def, Var->getDeclContext(), getTemplateInstantiationArgs(Var))); - CurContext = PreviousContext; if (Var) { + Var->setPreviousDeclaration(OldVar); + Var->setTemplateSpecializationKind(OldVar->getTemplateSpecializationKind()); DeclGroupRef DG(Var); Consumer.HandleTopLevelDecl(DG); } diff --git a/test/CXX/temp/temp.spec/temp.explicit/p1-emit.cpp b/test/CXX/temp/temp.spec/temp.explicit/p1-emit.cpp new file mode 100644 index 0000000000..f2282a2395 --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.explicit/p1-emit.cpp @@ -0,0 +1,26 @@ +// RUN: clang-cc -emit-llvm -triple x86_64-apple-darwin10 -o - %s | FileCheck %s +template +struct X { + static T member1; + static T member2; + static T member3; +}; + +template +T X::member1; + +template +T X::member2 = 17; + +// CHECK: @_ZN1XIiE7member1E = global i32 0 +template int X::member1; + +// CHECK: @_ZN1XIiE7member2E = global i32 17 +template int X::member2; + +// For implicit instantiation of +long& get(bool Cond) { + // CHECK: @_ZN1XIlE7member1E = weak global i64 0 + // CHECK: @_ZN1XIlE7member2E = weak global i64 17 + return Cond? X::member1 : X::member2; +} \ No newline at end of file