llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.11k stars 12.01k forks source link

Visibility annotation on partial template specialization is ignored #116414

Open DKLoehr opened 4 hours ago

DKLoehr commented 4 hours ago

If we have a partial template specialization of a class, the visibility of its static members is determined by the visibility attribute of the original definition, rather than the partially-templated one:

#define HIDDEN __attribute__((visibility("hidden")))

template<class T>
struct S { static int n; };

template<class T>
struct HIDDEN S<T*> { static int n; };

template <class T>
int S<T*>::n = 4;

template struct S<int*>;

// $ clang++ -c test.cpp
// $ readelf -sW test.o | grep _ZN1
//    2: 0000000000000000     4 OBJECT  WEAK   DEFAULT    4 _ZN1SIPiE1nE

My expectation is that S<int*>::n should be hidden here, because the partial specialization was declared hidden. This is also what happens if we compile with gcc instead of clang. We get the same issue if we declare S to be HIDDEN in its original definition -- S<int*>::n will be hidden, even if S<T*> is explicitly given default visibility.

Putting a visibility attribute directly on the definition of int S<T*>::n = 4; correctly overrides previously-declared attributes.

This only seems to happen for partial specializations: if we specialize S<int*> explicitly, its visibility attribute is taken into account.

This seems closely related to #103477, though I'm not sure if it's exactly the same issue or not. Oddly, it seems that the visibility of int S<T*>::n; is considered to be hidden at some point during compilation, since it's triggering a warning I'm developing that involves checking for hidden visibility. This indicates that the attribute is getting lost at some point, as mentioned here.

llvmbot commented 4 hours ago

@llvm/issue-subscribers-clang-codegen

Author: Devon Loehr (DKLoehr)

If we have a partial template specialization of a class, the visibility of its static members is determined by the visibility attribute of the original definition, rather than the partially-templated one: ```C++ #define HIDDEN __attribute__((visibility("hidden"))) template<class T> struct S { static int n; }; template<class T> struct HIDDEN S<T*> { static int n; }; template <class T> int S<T*>::n = 4; template struct S<int*>; // $ clang++ -c test.cpp // $ readelf -sW test.o | grep _ZN1 // 2: 0000000000000000 4 OBJECT WEAK DEFAULT 4 _ZN1SIPiE1nE ``` My expectation is that `S<int*>::n` should be hidden here, because the partial specialization was declared hidden. This is also what happens if we compile with gcc instead of clang. We get the same issue if we declare S to be HIDDEN in its original definition -- `S<int*>::n` will be hidden, even if `S<T*>` is explicitly given default visibility. Putting a visibility attribute directly on the definition of `int S<T*>::n = 4;` correctly overrides previously-declared attributes. This only seems to happen for partial specializations: if we specialize `S<int*>` explicitly, its visibility attribute is taken into account. This seems closely related to #103477, though I'm not sure if it's exactly the same issue or not. Oddly, it seems that the visibility of `int S<T*>::n;` is considered to be hidden at some point during compilation, since it's triggering a warning I'm developing that involves checking for hidden visibility. This indicates that the attribute is getting lost at some point, [as mentioned here](https://github.com/llvm/llvm-project/issues/103477#issuecomment-2329253191).