]> granicus.if.org Git - clang/commitdiff
A couple of tweaks to the visibility rules:
authorJohn McCall <rjmccall@apple.com>
Tue, 26 Oct 2010 04:59:26 +0000 (04:59 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 26 Oct 2010 04:59:26 +0000 (04:59 +0000)
  - tags with C linkage should ignore visibility=hidden
  - functions and variables with explicit visibility attributes should
    ignore the linkage of their types
Either of these should be sufficient to fix PR8457.

Also, FileCheck-ize a test case.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@117351 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclBase.h
lib/AST/Decl.cpp
lib/AST/DeclBase.cpp
test/CodeGen/visibility.c
test/CodeGenCXX/visibility.cpp

index 073efa69026a929f067901ca0b30a6bcb5df13b9..cc4e41abfe1e6ce057fb83433c174fa05f88fdde 100644 (file)
@@ -816,6 +816,10 @@ public:
   /// C++0x scoped enums), and C++ linkage specifications.
   bool isTransparentContext() const;
 
+  /// \brief Determines whether this context is, or is nested within,
+  /// a C++ extern "C" linkage spec.
+  bool isExternCContext() const;
+
   /// \brief Determine whether this declaration context is equivalent
   /// to the declaration context DC.
   bool Equals(const DeclContext *DC) const {
index 42e762d56ab676fc5eb4b0ad7dfd445cf2bef10c..5cc745d40ded9363ca781b5b4b33a1c21e976cae 100644 (file)
@@ -212,9 +212,13 @@ static LVPair getLVForNamespaceScopeDecl(const NamedDecl *D) {
     //   given variable or function shall be identical...
     // C does not have an equivalent rule.
     //
+    // Ignore this if we've got an explicit attribute;  the user
+    // probably knows what they're doing.
+    //
     // 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 && !Var->isExternC()) {
+    if (Context.getLangOptions().CPlusPlus && !Var->isExternC() &&
+        !Var->hasAttr<VisibilityAttr>()) {
       LVPair TypeLV = Var->getType()->getLinkageAndVisibility();
       if (TypeLV.first != ExternalLinkage)
         return LVPair(UniqueExternalLinkage, DefaultVisibility);
@@ -247,7 +251,8 @@ static LVPair getLVForNamespaceScopeDecl(const NamedDecl *D) {
   } else if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
     // Modify the function's LV by the LV of its type unless this is
     // C or extern "C".  See the comment above about variables.
-    if (Context.getLangOptions().CPlusPlus && !Function->isExternC()) {
+    if (Context.getLangOptions().CPlusPlus && !Function->isExternC() &&
+        !Function->hasAttr<VisibilityAttr>()) {
       LVPair TypeLV = Function->getType()->getLinkageAndVisibility();
       if (TypeLV.first != ExternalLinkage)
         return LVPair(UniqueExternalLinkage, DefaultVisibility);
@@ -321,8 +326,11 @@ static LVPair getLVForNamespaceScopeDecl(const NamedDecl *D) {
         ConsiderDashFVisibility = false;
     }
 
+    // Consider -fvisibility unless the type has C linkage.
     if (ConsiderDashFVisibility)
-      ConsiderDashFVisibility = Tag->isDefinition();
+      ConsiderDashFVisibility =
+        (Context.getLangOptions().CPlusPlus &&
+         !Tag->getDeclContext()->isExternCContext());
 
   //     - an enumerator belonging to an enumeration with external linkage;
   } else if (isa<EnumConstantDecl>(D)) {
index 483352f2d623bf696ed9b1d9f7717061c566b968..5606d54478fe0c75d839dea4f0fe7f04e86eefd6 100644 (file)
@@ -529,6 +529,17 @@ bool DeclContext::isTransparentContext() const {
   return false;
 }
 
+bool DeclContext::isExternCContext() const {
+  const DeclContext *DC = this;
+  while (DC->DeclKind != Decl::TranslationUnit) {
+    if (DC->DeclKind == Decl::LinkageSpec)
+      return cast<LinkageSpecDecl>(DC)->getLanguage()
+        == LinkageSpecDecl::lang_c;
+    DC = DC->getParent();
+  }
+  return false;
+}
+
 bool DeclContext::Encloses(const DeclContext *DC) const {
   if (getPrimaryContext() != this)
     return getPrimaryContext()->Encloses(DC);
index 8f81c8f3a99009862da37865ff1679801c40218b..501071b832b0550dbf9ecfb25f29fd5bffcab099 100644 (file)
@@ -1,33 +1,34 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fvisibility default -emit-llvm -o %t %s
-// RUN: grep '@g_com = common global i32 0' %t
-// RUN: grep '@g_def = global i32 0' %t
-// RUN: grep '@g_ext = external global i32' %t
-// RUN: grep '@g_deferred = internal global' %t
-// RUN: grep 'declare void @f_ext()' %t
-// RUN: grep 'define internal void @f_deferred()' %t
-// RUN: grep 'define i32 @f_def()' %t
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fvisibility protected -emit-llvm -o %t %s
-// RUN: grep '@g_com = common protected global i32 0' %t
-// RUN: grep '@g_def = protected global i32 0' %t
-// RUN: grep '@g_ext = external global i32' %t
-// RUN: grep '@g_deferred = internal global' %t
-// RUN: grep 'declare void @f_ext()' %t
-// RUN: grep 'define internal void @f_deferred()' %t
-// RUN: grep 'define protected i32 @f_def()' %t
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fvisibility hidden -emit-llvm -o %t %s
-// RUN: grep '@g_com = common hidden global i32 0' %t
-// RUN: grep '@g_def = hidden global i32 0' %t
-// RUN: grep '@g_ext = external global i32' %t
-// RUN: grep '@g_deferred = internal global' %t
-// RUN: grep 'declare void @f_ext()' %t
-// RUN: grep 'define internal void @f_deferred()' %t
-// RUN: grep 'define hidden i32 @f_def()' %t
+// RUN: %clang_cc1 %s -triple i386-unknown-unknown -fvisibility default -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-DEFAULT
+// RUN: %clang_cc1 %s -triple i386-unknown-unknown -fvisibility protected -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-PROTECTED
+// RUN: %clang_cc1 %s -triple i386-unknown-unknown -fvisibility hidden -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-HIDDEN
 
+// CHECK-DEFAULT: @g_def = global i32 0
+// CHECK-DEFAULT: @g_com = common global i32 0
+// CHECK-DEFAULT: @g_ext = external global i32
+// CHECK-DEFAULT: @g_deferred = internal global
+// CHECK-PROTECTED: @g_def = protected global i32 0
+// CHECK-PROTECTED: @g_com = common protected global i32 0
+// CHECK-PROTECTED: @g_ext = external global i32
+// CHECK-PROTECTED: @g_deferred = internal global
+// CHECK-HIDDEN: @g_def = hidden global i32 0
+// CHECK-HIDDEN: @g_com = common hidden global i32 0
+// CHECK-HIDDEN: @g_ext = external global i32
+// CHECK-HIDDEN: @g_deferred = internal global
 int g_com;
 int g_def = 0;
 extern int g_ext;
 static char g_deferred[] = "hello";
 
+// CHECK-DEFAULT: define i32 @f_def()
+// CHECK-DEFAULT: declare void @f_ext()
+// CHECK-DEFAULT: define internal void @f_deferred()
+// CHECK-PROTECTED: define protected i32 @f_def()
+// CHECK-PROTECTED: declare void @f_ext()
+// CHECK-PROTECTED: define internal void @f_deferred()
+// CHECK-HIDDEN: define hidden i32 @f_def()
+// CHECK-HIDDEN: declare void @f_ext()
+// CHECK-HIDDEN: define internal void @f_deferred()
+
 extern void f_ext(void);
 
 static void f_deferred(void) {
@@ -38,3 +39,10 @@ int f_def(void) {
   f_deferred();
   return g_com + g_def + g_ext + g_deferred[0];
 }
+
+// PR8457
+// CHECK-DEFAULT: define void @test1(
+// CHECK-PROTECTED: define void @test1(
+// CHECK-HIDDEN: define void @test1(
+struct Test1 { int field; };
+void  __attribute__((visibility("default"))) test1(struct Test1 *v) { }
index 0eb075c999d7447a020c809e62290f560d639c00..9e61af1397591520b263752c034a72d46512c82e 100644 (file)
@@ -8,6 +8,8 @@
 // CHECK: @_ZN5Test425VariableInHiddenNamespaceE = hidden global i32 10
 // CHECK: @_ZN5Test71aE = hidden global
 // CHECK: @_ZN5Test71bE = global
+// CHECK: @test9_var = global
+// CHECK-HIDDEN: @test9_var = global
 // CHECK: @_ZTVN5Test63fooE = weak_odr hidden constant 
 
 namespace Test1 {
@@ -115,3 +117,19 @@ namespace Test8 {
     bar();
   }
 }
+
+// PR8457
+namespace Test9 {
+  extern "C" {
+    struct A { int field; };
+    void DEFAULT test9_fun(struct A *a) { }
+    struct A DEFAULT test9_var; // above
+  }
+  // CHECK: define void @test9_fun(
+  // CHECK-HIDDEN: define void @test9_fun(
+
+  void test() {
+    A a = test9_var;
+    test9_fun(&a);
+  }
+}