/* { dg-require-visibility "" } */ /* { dg-options "-fvisibility=hidden" } */ /* { dg-final { scan-hidden "__ZN1s6vectorI1AEC1Ev" } } */ /* { dg-final { scan-hidden "__ZN1s3fooI1AEEvT_" } } */ /* Radar 5813435 */ namespace s __attribute__((visibility("default"))) { template <class T> class vector { public: vector() { } }; template <class T> void foo(T t) { } } class A { public: A() { } }; s::vector<A> v; int main() { A a; s::foo(a); } should pass (if I got the spelling for the symbols correct wrt leading _). radr://5813435
Confirmed. The reason why they should be hidden is because the template argument is hidden.
I think this is essentially invalid/by design, it's core visibility part of the issue we have been discussing lately with Vincenzo, for example: he sees the visibility("default") decorations on namespace std an annoyance exactly because of this behavior wrt user-code instantiations of namespace std templates. Is this core mechanism ever going to change? Jason?
Ah, need to use constrain_visibility_for_template for function templates as well as class templates.
No, I was misreading the code, it should already deal with function templates.
Ah! Let's keep Vincenzo up to date about this.
Author: jason Date: Mon Nov 7 04:40:14 2011 New Revision: 181069 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=181069 Log: PR c++/35688 gcc/c-common/ * c-common.c (decl_has_visibility_attr): Split out from... (c_determine_visibility): ...here. * c-common.h: Declare it. gcc/cp/ * decl2.c (constrain_visibility): Check decl_has_visibility_attr rather than DECL_VISIBILITY_SPECIFIED. Added: trunk/gcc/testsuite/g++.dg/ext/visibility/template7.C Modified: trunk/gcc/c-family/ChangeLog trunk/gcc/c-family/c-common.c trunk/gcc/c-family/c-common.h trunk/gcc/cp/ChangeLog trunk/gcc/cp/decl2.c trunk/gcc/testsuite/ChangeLog
The situation now is even more confused. Most of the std algos have iterators as arguments. Visibility seems not to propagate there.. below is my extensive test compile as c++ -O0 -shared -fPIC -fvisibility=hidden -o bha.so testICF.cpp nm bha.so | c++filt and look at the mixture of " t " and " T " for instance 000000000000ad7f t void std::__adjust_heap<__gnu_cxx::__normal_iterator<C**, std::vector<C*, std::allocator<C*> > >, long, C*>(__gnu_cxx::__normal_iterator<C**, std::vector<C*, std::allocator<C*> > >, long, long, C*) 000000000000afea T void std::__iter_swap<true>::iter_swap<__gnu_cxx::__normal_iterator<C**, std::vector<C*, std::allocator<C*> > >, __gnu_cxx::__normal_iterator<C**, std::vector<C*, std::allocator<C*> etc cat testICF.cpp #include<vector> #include<algorithm> #include<cmath> struct A { A(float q=0): v(q){} float v; bool operator<(A const & a) const { return v<a.v;} }; struct B { B(float q=0): v(q){} bool operator<(B const & a) const { return v<a.v;} float v; }; struct C { C(double q=0): v(q){} bool operator<(C const & a) const { return v<a.v;} double v; }; float cosq(A const & a) { return cos(a.v); } float cosq(B const & a) { return cos(a.v); } template<typename T> int game(std::vector<T> const & a, std::vector<T> & b) { typedef typename std::vector<T>::const_iterator Iter; for (Iter i=a.begin(); i!=a.end(); ++i) { if ( (*i).v>0.) b.push_back((*i).v+1); } std::sort(b.begin(),b.end()); return b.size(); } template<typename T> int gamep(std::vector<T*> const & a, std::vector<T*> & b) { typedef typename std::vector<T*>::const_iterator Iter; for (Iter i=a.begin(); i!=a.end(); ++i) { if ( (*i)->v>0.) b.push_back((*i)); } std::sort(b.begin(),b.end()); return b.size(); } namespace data /* __attribute__((visibility("default"))) */ { std::vector<A> a; std::vector<B> b; std::vector<C> c; std::vector<A*> ap; std::vector<B*> bp; std::vector<C*> cp; } #include<iostream> int __attribute__((visibility("default"))) go() { using namespace data; int ret=0; try { ret+=game(a,a); ret+=game(b,b); ret+=game(c,c); ret+=gamep(ap,ap); ret+=gamep(bp,bp); ret+=gamep(cp,cp); } catch (std::exception & ce) { std::cout << ce.what() << std::endl; } return ret; }
Reduced test actually it is enough to add s::foo(v); in the main after s::foo(a); to get 0000000000000e34 t void s::foo<A>(A) 0000000000000e3a T void s::foo<s::vector<A> >(s::vector<A>) 0000000000000e2a t s::vector<A>::vector()
Author: jason Date: Mon Nov 7 17:51:40 2011 New Revision: 181102 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=181102 Log: PR c++/35688 * decl2.c (constrain_visibility): Return void. Add tmpl parm which gives the constraint priority over an attribute. (constrain_visibility_for_template, determine_visibility): Adjust. * pt.c (instantiate_class_template_1): Call determine_visibility. Added: trunk/gcc/testsuite/g++.dg/ext/visibility/template8.C Modified: trunk/gcc/c-family/ChangeLog trunk/gcc/c-family/c-common.c trunk/gcc/c-family/c-common.h trunk/gcc/cp/ChangeLog trunk/gcc/cp/decl2.c trunk/gcc/cp/pt.c trunk/gcc/testsuite/ChangeLog
Created attachment 25744 [details] patch for 4.6 I think I'm not going to apply this to release branches, as it's a pretty significant change in behavior, but here's a patch against 4.6 if people want to apply it locally.
Fixed for 4.7.
much better! for the test in comment 7 now I get c++ -O0 -shared -fPIC -fvisibility=hidden -o bha.so testICF.cpp nm bha.so | c++filt | grep " T " 0000000000000e5c T go() 00000000000069ce T unsigned long const& std::max<unsigned long>(unsigned long const&, unsigned long const&) 00000000000010b2 T std::__lg(long) 00000000000010d8 T operator new(unsigned long, void*) puzzled by the visibility of std::max above I modified the original test further as cat visTest.cc namespace s __attribute__((visibility("default"))) { template <class T> class vector { public: vector() { } }; template <class T> void foo(T t) { } } class A { public: A() { } s::vector<int> v; }; s::vector<A> v; int main() { A a; s::foo(a); s::foo(v); s::foo(a.v); return 0; } and I get default visibility for s::vector<int>, as expected. not necessarily as wished… In my opinion this is consistent with spec, expect bug reports though! c++ -O0 -shared -fPIC -fvisibility=hidden -o bha.so visTest.cc nm bha.so | c++filt 0000000000000d77 t __GLOBAL__sub_I_visTest.cc 0000000000000d4c t __static_initialization_and_destruction_0(int, int) 0000000000000d8c t A::A() 0000000000000dba t void s::foo<A>(A) 0000000000000dc0 t void s::foo<s::vector<A> >(s::vector<A>) 0000000000000dc6 T void s::foo<s::vector<int> >(s::vector<int>) 0000000000000db0 t s::vector<A>::vector() 0000000000000da6 T s::vector<int>::vector() 0000000000000d04 t __dyld_func_lookup 0000000000000d0a t _main 0000000000001038 d _v U dyld_stub_binder 0000000000000cf0 t dyld_stub_binding_helper p.s I verified that if "s" in NOT default visible vector<int> will become hidden
HI, I've the following problem with this: 3rd party libs don't "export" their templates, e. g. Boost doesn't export shared_ptr. So if I use a shared_ptr as template parameter the template itself isn't exported. I don't understand why fully declared templates like Boost's shared_ptr have a visibility at all. I know that C++11's shared_ptr is exported but that doesn't help me now. Do I've to change my code or is this a regression or is it Boost's fault? Cheers, André
Could anyone reply to my last question? Thanks in advance.
(In reply to comment #13) > I don't understand why fully declared templates like Boost's shared_ptr have a > visibility at all. I'm not sure why they would. Are you compiling with -fvisibility=hidden? If so, you need to explicitly give everything that you want to be exported the appropriate visibility. > Do I've to change my code or is this a regression or is it Boost's fault? You might wrap the #includes in #pragma GCC visibility push(default) #pragma GCC visibility pop to give the Boost library all default visibility.