From: John McCall Date: Tue, 26 Oct 2010 04:59:26 +0000 (+0000) Subject: A couple of tweaks to the visibility rules: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ac65c6208d48b0f9b4661c30c28997a280ac5ba6;p=clang A couple of tweaks to the visibility rules: - 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 --- diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 073efa6902..cc4e41abfe 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -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 { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 42e762d56a..5cc745d40d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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()) { 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(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()) { 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(D)) { diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 483352f2d6..5606d54478 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -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(DC)->getLanguage() + == LinkageSpecDecl::lang_c; + DC = DC->getParent(); + } + return false; +} + bool DeclContext::Encloses(const DeclContext *DC) const { if (getPrimaryContext() != this) return getPrimaryContext()->Encloses(DC); diff --git a/test/CodeGen/visibility.c b/test/CodeGen/visibility.c index 8f81c8f3a9..501071b832 100644 --- a/test/CodeGen/visibility.c +++ b/test/CodeGen/visibility.c @@ -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) { } diff --git a/test/CodeGenCXX/visibility.cpp b/test/CodeGenCXX/visibility.cpp index 0eb075c999..9e61af1397 100644 --- a/test/CodeGenCXX/visibility.cpp +++ b/test/CodeGenCXX/visibility.cpp @@ -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); + } +}