summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2016-07-02 01:32:16 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2016-07-02 01:32:16 +0000
commitd9b9009c611a03b47b40f780de5562b141810682 (patch)
tree8b850ec1d6b4ca2c214a840077cd0b9827d0c880
parent27cdf401eaa84e0d4ae82d174ddba6453e882856 (diff)
downloadbcm5719-llvm-d9b9009c611a03b47b40f780de5562b141810682.tar.gz
bcm5719-llvm-d9b9009c611a03b47b40f780de5562b141810682.zip
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. llvm-svn: 274416
-rw-r--r--clang/include/clang/AST/ASTContext.h16
-rw-r--r--clang/lib/AST/ASTContext.cpp37
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp14
-rw-r--r--clang/test/CodeGenCXX/cxx1z-inline-variables.cpp20
4 files changed, 83 insertions, 4 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index b4f232bbdc5..93a0b48133c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -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,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 564b93453ef..0414c7a9faf 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5146,6 +5146,27 @@ bool ASTContext::isMSStaticDataMemberInlineDefinition(const VarDecl *VD) const {
!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 basicGVALinkageForVariable(const ASTContext &Context,
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:
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index fb1763551a3..c6e20a5f9c7 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1447,6 +1447,12 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// 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(GlobalDecl GD) {
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
diff --git a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
index 90d0ed61311..183709373d1 100644
--- a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
+++ b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
@@ -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;
OpenPOWER on IntegriCloud