Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2283,6 +2283,10 @@ void getObjCEncodingForMethodParameter(Decl::ObjCDeclQualifier QT, QualType T, std::string& S, bool Extended) const; + + /// \brief Returns true if this is an inline-initialized static data member + /// which the MS ABI treats as a definition. + bool isMSStaticDataMemberInlineDefinition(const VarDecl *VD) const; private: const ASTRecordLayout & Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -4781,6 +4781,14 @@ return sz; } +bool ASTContext::isMSStaticDataMemberInlineDefinition(const VarDecl *VD) const { + return VD->isStaticDataMember() && + VD->getType()->isIntegralOrEnumerationType() && + getTargetInfo().getCXXABI().isMicrosoft() && + !VD->getFirstDecl()->isOutOfLine() && + VD->getFirstDecl()->hasInit(); +} + static inline std::string charUnitsToString(const CharUnits &CU) { return llvm::itostr(CU.getQuantity()); @@ -7849,6 +7857,12 @@ : StaticLocalLinkage; } + // MSVC treats in-class initialized static data members as definitions. + // By giving them non-strong linkage, out-of-line definitions won't + // cause link errors. + if (Context.isMSStaticDataMemberInlineDefinition(VD)) + return GVA_DiscardableODR; + switch (VD->getTemplateSpecializationKind()) { case TSK_Undeclared: case TSK_ExplicitSpecialization: @@ -7934,7 +7948,8 @@ const VarDecl *VD = cast(D); assert(VD->isFileVarDecl() && "Expected file scoped var"); - if (VD->isThisDeclarationADefinition() == VarDecl::DeclarationOnly) + if (VD->isThisDeclarationADefinition() == VarDecl::DeclarationOnly && + !isMSStaticDataMemberInlineDefinition(VD)) return false; // Variables that can be needed in other TUs are required. Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -403,10 +403,6 @@ /// Gets the deleted virtual member call name. virtual StringRef GetDeletedVirtualCallName() = 0; - /// \brief Returns true iff static data members that are initialized in the - /// class definition should have linkonce linkage. - virtual bool isInlineInitializedStaticDataMemberLinkOnce() { return false; } - /**************************** Array cookies ******************************/ /// Returns the extra size required in order to store the array Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1256,7 +1256,8 @@ const auto *VD = cast(Global); assert(VD->isFileVarDecl() && "Cannot emit local var decl as global."); - if (VD->isThisDeclarationADefinition() != VarDecl::Definition) + if (VD->isThisDeclarationADefinition() != VarDecl::Definition && + !Context.isMSStaticDataMemberInlineDefinition(VD)) return; } @@ -1592,18 +1593,6 @@ return true; } -static bool isVarDeclInlineInitializedStaticDataMember(const VarDecl *VD) { - if (!VD->isStaticDataMember()) - return false; - const VarDecl *InitDecl; - const Expr *InitExpr = VD->getAnyInitializer(InitDecl); - if (!InitExpr) - return false; - if (InitDecl->isThisDeclarationADefinition()) - return false; - return true; -} - /// GetOrCreateLLVMGlobal - If the specified mangled name is not in the module, /// create and return an llvm GlobalVariable with the specified type. If there /// is something in the module with the specified name, return it potentially @@ -1666,9 +1655,9 @@ // If required by the ABI, treat declarations of static data members with // inline initializers as definitions. - if (getCXXABI().isInlineInitializedStaticDataMemberLinkOnce() && - isVarDeclInlineInitializedStaticDataMember(D)) + if (getContext().isMSStaticDataMemberInlineDefinition(D)) { EmitGlobalVarDefinition(D); + } // Handle XCore specific ABI requirements. if (getTarget().getTriple().getArch() == llvm::Triple::xcore && @@ -2087,18 +2076,6 @@ return !Context.getLangOpts().AppleKext ? llvm::Function::WeakODRLinkage : llvm::Function::ExternalLinkage; - // If required by the ABI, give definitions of static data members with inline - // initializers at least linkonce_odr linkage. - auto const VD = dyn_cast(D); - if (getCXXABI().isInlineInitializedStaticDataMemberLinkOnce() && - VD && isVarDeclInlineInitializedStaticDataMember(VD)) { - if (VD->hasAttr()) - return llvm::GlobalValue::AvailableExternallyLinkage; - if (VD->hasAttr()) - return llvm::GlobalValue::WeakODRLinkage; - return llvm::GlobalValue::LinkOnceODRLinkage; - } - // C++ doesn't have tentative definitions and thus cannot have common // linkage. if (!getLangOpts().CPlusPlus && isa(D) && Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -55,8 +55,6 @@ // arbitrary. StringRef GetDeletedVirtualCallName() override { return "_purecall"; } - bool isInlineInitializedStaticDataMemberLinkOnce() override { return true; } - llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF, llvm::Value *ptr, QualType type) override; Index: lib/CodeGen/ModuleBuilder.cpp =================================================================== --- lib/CodeGen/ModuleBuilder.cpp +++ lib/CodeGen/ModuleBuilder.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGen/ModuleBuilder.h" +#include "CGCXXABI.h" #include "CGDebugInfo.h" #include "CodeGenModule.h" #include "clang/AST/ASTContext.h" @@ -128,6 +129,19 @@ return; Builder->UpdateCompletedType(D); + + // If required by the ABI, treat declarations of static data members with + // inline initializers as definitions. + if (Ctx->getTargetInfo().getCXXABI().isMicrosoft()) { + for (Decl *Member : D->decls()) { + if (VarDecl *VD = dyn_cast(Member)) { + if (Ctx->isMSStaticDataMemberInlineDefinition(VD) && + Ctx->DeclMustBeEmitted(VD)) { + Builder->EmitGlobal(VD); + } + } + } + } } void HandleTagDeclRequiredDefinition(const TagDecl *D) override { Index: test/CodeGenCXX/ms-integer-static-data-members-exported.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/ms-integer-static-data-members-exported.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -emit-llvm -triple=i386-pc-win32 %s -o - | FileCheck %s + +enum Enum { zero, one, two }; + +struct __declspec(dllexport) S { + // In the MS ABI, this counts as a definition. + // Since it is exported, it must be emitted even if it's unreferenced. + static const short x = 42; + + // This works for enums too. + static const Enum y = two; + + struct NonExported { + // dllexport is not inherited by this nested class. + // Since z is not referenced, it should not be emitted. + static const int z = 42; + }; +}; + +// CHECK: @"\01?x@S@@2FB" = weak_odr dllexport constant i16 42, align 2 +// CHECK: @"\01?y@S@@2W4Enum@@B" = weak_odr dllexport constant i32 2, align 4 +// CHECK-NOT: NonExported