GCC faithfully calculates visibility for variables independently of
whether it's a declaration or not, then ignores that information for
declarations unless it was explicitly given.  It's not totally clear
how that should be mapped into a sane system, but make an effort.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@117780 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index dbd14e0..2d29a6a 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -255,21 +255,17 @@
     //
     // Note that we don't want to make the variable non-external
     // because of this, but unique-external linkage suits us.
-    if (Context.getLangOptions().CPlusPlus && !ExplicitVisibility &&
-        !Var->isExternC()) {
+    if (Context.getLangOptions().CPlusPlus && !Var->isExternC()) {
       LVPair TypeLV = Var->getType()->getLinkageAndVisibility();
       if (TypeLV.first != ExternalLinkage)
         return LVPair(UniqueExternalLinkage, DefaultVisibility);
-
-      // Otherwise, ignore type visibility for declarations.
-      if (!isDeclaration)
+      if (!isDeclaration && !ExplicitVisibility)
         LV.second = minVisibility(LV.second, TypeLV.second);
     }
 
     // Don't consider -fvisibility for pure declarations.
-    if (isDeclaration) {
+    if (isDeclaration)
       ConsiderGlobalVisibility = false;
-    }
 
     if (!Context.getLangOptions().CPlusPlus &&
         (Var->getStorageClass() == SC_Extern ||
@@ -524,24 +520,24 @@
 
   // Static data members.
   } else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
-    // If we don't have explicit visibility information in the
-    // hierarchy, apply the LV from its type.  See the comment about
-    // namespace-scope variables for justification for this
-    // optimization.
-    if (!HasExplicitVisibility) {
-      LVPair TypeLV = VD->getType()->getLinkageAndVisibility();
-      if (TypeLV.first != ExternalLinkage)
-        LV.first = minLinkage(LV.first, UniqueExternalLinkage);
-      LV.second = minVisibility(LV.second, TypeLV.second);
+    bool IsDefinition = (VD->getDefinition() &&
+                         VD->getTemplateSpecializationKind()
+                           != TSK_ExplicitInstantiationDeclaration);
+
+    // GCC just ignores the visibility of a variable declaration
+    // unless it's explicit.
+    if (!IsDefinition && !HasExplicitVisibility) {
+      LV.second = DefaultVisibility;
+      ConsiderGlobalVisibility = false;
     }
 
-    // Ignore global visibility if it's an extern template or
-    // just a declaration.
-    if (ConsiderGlobalVisibility)
-      ConsiderGlobalVisibility =
-        (VD->getDefinition() &&
-         VD->getTemplateSpecializationKind()
-           != TSK_ExplicitInstantiationDeclaration);
+    // Modify the variable's linkage by its type, but ignore the
+    // type's visibility unless it's a definition.
+    LVPair TypeLV = VD->getType()->getLinkageAndVisibility();
+    if (TypeLV.first != ExternalLinkage)
+      LV.first = minLinkage(LV.first, UniqueExternalLinkage);
+    if (IsDefinition && !HasExplicitVisibility)
+      LV.second = minVisibility(LV.second, TypeLV.second);
   }
 
   // Suppress -fvisibility if we have explicit visibility on any of
diff --git a/test/CodeGenCXX/visibility.cpp b/test/CodeGenCXX/visibility.cpp
index a555445..2272d1f 100644
--- a/test/CodeGenCXX/visibility.cpp
+++ b/test/CodeGenCXX/visibility.cpp
@@ -20,6 +20,8 @@
 // CHECK-HIDDEN: @_ZN6Test131C1aE = global
 // CHECK: @_ZN6Test143varE = external global
 // CHECK-HIDDEN: @_ZN6Test143varE = external global
+// CHECK: @_ZN6Test154TempINS_1AEE5Inner6bufferE = external global [0 x i8]
+// CHECK-HIDDEN: @_ZN6Test154TempINS_1AEE5Inner6bufferE = external global [0 x i8]
 // CHECK: @_ZTVN5Test63fooE = weak_odr hidden constant 
 
 namespace Test1 {
@@ -218,3 +220,17 @@
 
   struct A *test() { return var; }
 }
+
+// rdar://problem/8613093
+namespace Test15 {
+  struct A {};
+  template <class T> struct Temp {
+    struct Inner {
+      static char buffer[0];
+    };
+  };
+
+  char *test() {
+    return Temp<A>::Inner::buffer;
+  }
+}