https://github.com/asavonic created 
https://github.com/llvm/llvm-project/pull/136689

This patchset includes #136128, and adds a fix for regressions that were 
reported by @jplehr and @DKLoehr:

[clang] Fix computeTypeLinkageInfo for non-record member pointers

MemberPointerType can point to records, functions, or types from
template parameters. computeTypeLinkageInfo used to expect only
records, and crash for anything else. It seems that the compiler never
executed this code path before patch 
https://github.com/llvm/llvm-project/pull/136128 where the issue was
reported.

Function member (test74):

    MemberPointerType 'type-parameter-0-0 (type-parameter-0-1::*)(void)' 
dependent
    |-TemplateTypeParmType 'type-parameter-0-1' dependent depth 0 index 1
    `-FunctionProtoType 'type-parameter-0-0 (void)' dependent cdecl
      `-TemplateTypeParmType 'type-parameter-0-0' dependent depth 0 index 0

Template parameter (test75):

    MemberPointerType 'type-parameter-0-1 type-parameter-0-0::*' dependent
    |-TemplateTypeParmType 'type-parameter-0-0' dependent depth 0 index 0
    `-TemplateTypeParmType 'type-parameter-0-1' dependent depth 0 index 1

For non-record types, it should be enough to look at a pointee type to
determine linkage and visibility.

LIT tests 74 and 75 are reduced from Chromium and LLVM libc test
harness as reported in https://github.com/llvm/llvm-project/pull/136128.


>From 677653091bf4ca585c27b1ad30edfa94fe5d0b90 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <andrew.savonic...@gmail.com>
Date: Mon, 21 Apr 2025 19:45:05 +0900
Subject: [PATCH 1/2] [clang] Fix computeTypeLinkageInfo for non-record member
 pointers

MemberPointerType can point to records, functions, or types from
template parameters. computeTypeLinkageInfo used to expect only
records, and crash for anything else. It seems that the compiler never
executed this code path before patch #136128 where the issue was
reported.

Function member (test74):

    MemberPointerType 'type-parameter-0-0 (type-parameter-0-1::*)(void)' 
dependent
    |-TemplateTypeParmType 'type-parameter-0-1' dependent depth 0 index 1
    `-FunctionProtoType 'type-parameter-0-0 (void)' dependent cdecl
      `-TemplateTypeParmType 'type-parameter-0-0' dependent depth 0 index 0

Template parameter (test75):

    MemberPointerType 'type-parameter-0-1 type-parameter-0-0::*' dependent
    |-TemplateTypeParmType 'type-parameter-0-0' dependent depth 0 index 0
    `-TemplateTypeParmType 'type-parameter-0-1' dependent depth 0 index 1

For non-record types, it should be enough to look at a pointee type to
determine linkage and visibility.

LIT tests 74 and 75 are reduced from Chromium and LLVM libc test
harness as reported in #136128.
---
 clang/lib/AST/Type.cpp               |  6 +++--
 clang/test/CodeGenCXX/visibility.cpp | 37 ++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index fe1dc7e2fe786..5783f87012731 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4787,8 +4787,10 @@ LinkageInfo 
LinkageComputer::computeTypeLinkageInfo(const Type *T) {
     return computeTypeLinkageInfo(cast<ReferenceType>(T)->getPointeeType());
   case Type::MemberPointer: {
     const auto *MPT = cast<MemberPointerType>(T);
-    LinkageInfo LV =
-        getDeclLinkageAndVisibility(MPT->getMostRecentCXXRecordDecl());
+    LinkageInfo LV;
+    if (CXXRecordDecl *D = MPT->getMostRecentCXXRecordDecl()) {
+      LV.merge(getDeclLinkageAndVisibility(D));
+    }
     LV.merge(computeTypeLinkageInfo(MPT->getPointeeType()));
     return LV;
   }
diff --git a/clang/test/CodeGenCXX/visibility.cpp 
b/clang/test/CodeGenCXX/visibility.cpp
index e1061f3dbd18f..b4c6fdcbcdf33 100644
--- a/clang/test/CodeGenCXX/visibility.cpp
+++ b/clang/test/CodeGenCXX/visibility.cpp
@@ -1463,3 +1463,40 @@ namespace test71 {
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden noundef i64 
@_ZN6test713fooIlE3zedEv(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden noundef i32 
@_ZN6test713fooIlE3barIiEET_v(
 }
+
+namespace test74 {
+  template <typename> struct T;
+  template <typename R>
+  struct T<void (R::*)()> {
+    template <typename M>
+    static __attribute__((__visibility__("hidden"))) void Invoke(M) {
+    }
+  };
+
+  struct C;
+  void (C::*MM)();
+
+  void Fun() {
+    T<decltype(MM)>::Invoke(0);
+  }
+  // CHECK-LABEL: define linkonce_odr void 
@_ZN6test741TIMNS_1CEFvvEE6InvokeIiEEvT_(
+  // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void 
@_ZN6test741TIMNS_1CEFvvEE6InvokeIiEEvT_(
+}
+
+namespace test75 {
+  template <class> struct T;
+  template <class C, class Ret>
+  struct T<Ret C::*> {
+    template <class M>
+    static __attribute__((__visibility__("hidden")))
+    void Invoke(M) {
+    }
+  };
+
+  struct A;
+  void Fun() {
+    T<void (A::*)()>::Invoke(0);
+  }
+  // CHECK-LABEL: define linkonce_odr void 
@_ZN6test751TIMNS_1AEFvvEE6InvokeIiEEvT_(
+  // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void 
@_ZN6test751TIMNS_1AEFvvEE6InvokeIiEEvT_(
+}

>From e0118064d9935dad6b3232c1c9503be603d92f87 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <andrew.savonic...@gmail.com>
Date: Thu, 17 Apr 2025 20:18:52 +0900
Subject: [PATCH 2/2] Reland [clang] Handle instantiated members to determine
 visibility (#136128)

As reported in issue #103477, visibility of instantiated member
functions used to be ignored when calculating visibility of a
specialization.

This patch modifies `getLVForClassMember` to look up for a source
template for an instantiated member, and changes `mergeTemplateLV` to
apply it.

A similar issue was reported in #31462, but it seems that `extern`
declaration with visibility prevents the function from being emitted
as hidden. This behavior seems correct, even though GCC emits it as
with default visibility instead.

Both tests from #103477 and #31462 are added as LIT tests `test72` and
`test73` respectively.
---
 clang/docs/ReleaseNotes.rst          |  1 +
 clang/lib/AST/Decl.cpp               | 13 +++++++--
 clang/test/CodeGenCXX/visibility.cpp | 42 ++++++++++++++++++++++++++--
 3 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 86d37f5616356..9ecd78c13d4e5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -458,6 +458,7 @@ Bug Fixes in This Version
 - Fixed a crash when ``#embed`` appears as a part of a failed constant
   evaluation. The crashes were happening during diagnostics emission due to
   unimplemented statement printer. (#GH132641)
+- Fixed visibility calculation for template functions. (#GH103477)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 1d9208f0e1c72..61d497999b669 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -400,9 +400,9 @@ void LinkageComputer::mergeTemplateLV(
   FunctionTemplateDecl *temp = specInfo->getTemplate();
   // Merge information from the template declaration.
   LinkageInfo tempLV = getLVForDecl(temp, computation);
-  // The linkage of the specialization should be consistent with the
-  // template declaration.
-  LV.setLinkage(tempLV.getLinkage());
+  // The linkage and visibility of the specialization should be
+  // consistent with the template declaration.
+  LV.mergeMaybeWithVisibility(tempLV, considerVisibility);
 
   // Merge information from the template parameters.
   LinkageInfo paramsLV =
@@ -1051,6 +1051,13 @@ LinkageComputer::getLVForClassMember(const NamedDecl *D,
     if (const auto *redeclTemp = dyn_cast<RedeclarableTemplateDecl>(temp)) {
       if (isExplicitMemberSpecialization(redeclTemp)) {
         explicitSpecSuppressor = temp->getTemplatedDecl();
+      } else if (const RedeclarableTemplateDecl *from =
+                     redeclTemp->getInstantiatedFromMemberTemplate()) {
+        // If no explicit visibility is specified yet, and this is an
+        // instantiated member of a template, look up visibility there
+        // as well.
+        LinkageInfo fromLV = from->getLinkageAndVisibility();
+        LV.mergeMaybeWithVisibility(fromLV, considerVisibility);
       }
     }
   }
diff --git a/clang/test/CodeGenCXX/visibility.cpp 
b/clang/test/CodeGenCXX/visibility.cpp
index b4c6fdcbcdf33..442e2a5aaa2b3 100644
--- a/clang/test/CodeGenCXX/visibility.cpp
+++ b/clang/test/CodeGenCXX/visibility.cpp
@@ -1457,13 +1457,49 @@ namespace test71 {
   // CHECK-LABEL: declare hidden noundef i32 @_ZN6test713fooIiE3zedEv(
   // CHECK-LABEL: define linkonce_odr noundef i32 
@_ZN6test713fooIiE3barIiEET_v(
   // CHECK-LABEL: define linkonce_odr hidden noundef i64 
@_ZN6test713fooIlE3zedEv(
-  // CHECK-LABEL: define linkonce_odr noundef i32 
@_ZN6test713fooIlE3barIiEET_v(
+  // CHECK-LABEL: define linkonce_odr hidden noundef i32 
@_ZN6test713fooIlE3barIiEET_v(
   // CHECK-HIDDEN-LABEL: declare hidden noundef i32 @_ZN6test713fooIiE3zedEv(
   // CHECK-HIDDEN-LABEL: define linkonce_odr noundef i32 
@_ZN6test713fooIiE3barIiEET_v(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden noundef i64 
@_ZN6test713fooIlE3zedEv(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden noundef i32 
@_ZN6test713fooIlE3barIiEET_v(
 }
 
+// https://github.com/llvm/llvm-project/issues/103477
+namespace test72 {
+  template <class a>
+  struct t {
+    template <int>
+    static HIDDEN void bar() {}
+  };
+
+  void test() {
+      t<char>::bar<1>();
+  }
+  // CHECK-LABEL: define linkonce_odr hidden void 
@_ZN6test721tIcE3barILi1EEEvv(
+  // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void 
@_ZN6test721tIcE3barILi1EEEvv(
+}
+
+// https://github.com/llvm/llvm-project/issues/31462
+namespace test73 {
+  template <class T> struct s {
+    template <class U>
+    __attribute__((__visibility__("hidden"))) U should_not_be_exported();
+  };
+
+  template <class T> template <class U> U s<T>::should_not_be_exported() {
+    return U();
+  }
+
+  extern template struct __attribute__((__visibility__("default"))) s<int>;
+
+  int f() {
+    s<int> o;
+    return o.should_not_be_exported<int>();
+  }
+  // CHECK-LABEL: define linkonce_odr noundef i32 
@_ZN6test731sIiE22should_not_be_exportedIiEET_v(
+  // CHECK-HIDDEN-LABEL: define linkonce_odr noundef i32 
@_ZN6test731sIiE22should_not_be_exportedIiEET_v(
+}
+
 namespace test74 {
   template <typename> struct T;
   template <typename R>
@@ -1479,7 +1515,7 @@ namespace test74 {
   void Fun() {
     T<decltype(MM)>::Invoke(0);
   }
-  // CHECK-LABEL: define linkonce_odr void 
@_ZN6test741TIMNS_1CEFvvEE6InvokeIiEEvT_(
+  // CHECK-LABEL: define linkonce_odr hidden void 
@_ZN6test741TIMNS_1CEFvvEE6InvokeIiEEvT_(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void 
@_ZN6test741TIMNS_1CEFvvEE6InvokeIiEEvT_(
 }
 
@@ -1497,6 +1533,6 @@ namespace test75 {
   void Fun() {
     T<void (A::*)()>::Invoke(0);
   }
-  // CHECK-LABEL: define linkonce_odr void 
@_ZN6test751TIMNS_1AEFvvEE6InvokeIiEEvT_(
+  // CHECK-LABEL: define linkonce_odr hidden void 
@_ZN6test751TIMNS_1AEFvvEE6InvokeIiEEvT_(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void 
@_ZN6test751TIMNS_1AEFvvEE6InvokeIiEEvT_(
 }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to