Author: rsmith Date: Fri Jul 1 20:32:16 2016 New Revision: 274416 URL: http://llvm.org/viewvc/llvm-project?rev=274416&view=rev Log: PR28394: For compatibility with c++11 and c++14, if a static constexpr data member is redundantly redeclared outside the class definition in code built in c++17 mode, ensure we emit a non-discardable definition of the data member for c++11 and c++14 compilations to use.
Modified: cfe/trunk/include/clang/AST/ASTContext.h cfe/trunk/lib/AST/ASTContext.cpp cfe/trunk/lib/CodeGen/CodeGenModule.cpp cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp Modified: cfe/trunk/include/clang/AST/ASTContext.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=274416&r1=274415&r2=274416&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/ASTContext.h (original) +++ cfe/trunk/include/clang/AST/ASTContext.h Fri Jul 1 20:32:16 2016 @@ -2528,7 +2528,21 @@ public: /// \brief Returns true if this is an inline-initialized static data member /// which is treated as a definition for MSVC compatibility. bool isMSStaticDataMemberInlineDefinition(const VarDecl *VD) const; - + + enum class InlineVariableDefinitionKind { + None, ///< Not an inline variable. + Weak, ///< Weak definition of inline variable. + WeakUnknown, ///< Weak for now, might become strong later in this TU. + Strong ///< Strong definition. + }; + /// \brief Determine whether a definition of this inline variable should + /// be treated as a weak or strong definition. For compatibility with + /// C++14 and before, for a constexpr static data member, if there is an + /// out-of-line declaration of the member, we may promote it from weak to + /// strong. + InlineVariableDefinitionKind + getInlineVariableDefinitionKind(const VarDecl *VD) const; + private: const ASTRecordLayout & getObjCLayout(const ObjCInterfaceDecl *D, Modified: cfe/trunk/lib/AST/ASTContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=274416&r1=274415&r2=274416&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp (original) +++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jul 1 20:32:16 2016 @@ -5146,6 +5146,27 @@ bool ASTContext::isMSStaticDataMemberInl !VD->getFirstDecl()->isOutOfLine() && VD->getFirstDecl()->hasInit(); } +ASTContext::InlineVariableDefinitionKind +ASTContext::getInlineVariableDefinitionKind(const VarDecl *VD) const { + if (!VD->isInline()) + return InlineVariableDefinitionKind::None; + + // In almost all cases, it's a weak definition. + auto *First = VD->getFirstDecl(); + if (!First->isConstexpr() || First->isInlineSpecified() || + !VD->isStaticDataMember()) + return InlineVariableDefinitionKind::Weak; + + // If there's a file-context declaration in this translation unit, it's a + // non-discardable definition. + for (auto *D : VD->redecls()) + if (D->getLexicalDeclContext()->isFileContext()) + return InlineVariableDefinitionKind::Strong; + + // If we've not seen one yet, we don't know. + return InlineVariableDefinitionKind::WeakUnknown; +} + static inline std::string charUnitsToString(const CharUnits &CU) { return llvm::itostr(CU.getQuantity()); @@ -8494,9 +8515,21 @@ static GVALinkage basicGVALinkageForVari if (Context.isMSStaticDataMemberInlineDefinition(VD)) return GVA_DiscardableODR; - GVALinkage StrongLinkage = GVA_StrongExternal; - if (VD->isInline()) + // Most non-template variables have strong linkage; inline variables are + // linkonce_odr or (occasionally, for compatibility) weak_odr. + GVALinkage StrongLinkage; + switch (Context.getInlineVariableDefinitionKind(VD)) { + case ASTContext::InlineVariableDefinitionKind::None: + StrongLinkage = GVA_StrongExternal; + break; + case ASTContext::InlineVariableDefinitionKind::Weak: + case ASTContext::InlineVariableDefinitionKind::WeakUnknown: StrongLinkage = GVA_DiscardableODR; + break; + case ASTContext::InlineVariableDefinitionKind::Strong: + StrongLinkage = GVA_StrongODR; + break; + } switch (VD->getTemplateSpecializationKind()) { case TSK_Undeclared: Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=274416&r1=274415&r2=274416&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original) +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Fri Jul 1 20:32:16 2016 @@ -1447,6 +1447,12 @@ bool CodeGenModule::MayBeEmittedEagerly( // Implicit template instantiations may change linkage if they are later // explicitly instantiated, so they should not be emitted eagerly. return false; + if (const auto *VD = dyn_cast<VarDecl>(Global)) + if (Context.getInlineVariableDefinitionKind(VD) == + ASTContext::InlineVariableDefinitionKind::WeakUnknown) + // A definition of an inline constexpr static data member may change + // linkage later if it's redeclared outside the class. + return false; // If OpenMP is enabled and threadprivates must be generated like TLS, delay // codegen for global variables, because they may be marked as threadprivate. if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS && @@ -1595,8 +1601,14 @@ void CodeGenModule::EmitGlobal(GlobalDec VD->hasAttr<CUDADeviceAttr>()); if (!MustEmitForCuda && VD->isThisDeclarationADefinition() != VarDecl::Definition && - !Context.isMSStaticDataMemberInlineDefinition(VD)) + !Context.isMSStaticDataMemberInlineDefinition(VD)) { + // If this declaration may have caused an inline variable definition to + // change linkage, make sure that it's emitted. + if (Context.getInlineVariableDefinitionKind(VD) == + ASTContext::InlineVariableDefinitionKind::Strong) + GetAddrOfGlobalVar(VD); return; + } } // Defer code generation to first use when possible, e.g. if this is an inline Modified: cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp?rev=274416&r1=274415&r2=274416&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp (original) +++ cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp Fri Jul 1 20:32:16 2016 @@ -24,6 +24,26 @@ int a = f(); inline int b = f(); int c = f(); +// For compatibility with C++11 and C++14, an out-of-line declaration of a +// static constexpr local variable promotes the variable to weak_odr. +struct compat { + static constexpr int a = 1; + static constexpr int b = 2; + static constexpr int c = 3; + static inline constexpr int d = 4; +}; +const int &compat_use_before_redecl = compat::b; +const int compat::a; +const int compat::b; +const int compat::c; +const int compat::d; +const int &compat_use_after_redecl1 = compat::c; +const int &compat_use_after_redecl2 = compat::d; +// CHECK: @_ZN6compat1bE = weak_odr constant i32 2 +// CHECK: @_ZN6compat1aE = weak_odr constant i32 1 +// CHECK: @_ZN6compat1cE = weak_odr constant i32 3 +// CHECK: @_ZN6compat1dE = linkonce_odr constant i32 4 + template<typename T> struct X { static int a; static inline int b; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits