Bug 35688 - template visibility not overridden by template arguments
Summary: template visibility not overridden by template arguments
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.2.1
: P3 normal
Target Milestone: 4.7.0
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-03-25 00:17 UTC by Mike Stump
Modified: 2013-02-11 15:44 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2010-10-27 22:10:38


Attachments
patch for 4.6 (1.94 KB, patch)
2011-11-07 21:17 UTC, Jason Merrill
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Stump 2008-03-25 00:17:01 UTC
/* { 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
Comment 1 Andrew Pinski 2010-10-27 22:10:38 UTC
Confirmed.  The reason why they should be hidden is because the template argument is hidden.
Comment 2 Paolo Carlini 2011-11-06 12:13:47 UTC
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?
Comment 3 Jason Merrill 2011-11-06 13:20:22 UTC
Ah, need to use constrain_visibility_for_template for function templates as well as class templates.
Comment 4 Jason Merrill 2011-11-06 13:21:35 UTC
No, I was misreading the code, it should already deal with function templates.
Comment 5 Paolo Carlini 2011-11-06 21:12:52 UTC
Ah! Let's keep Vincenzo up to date about this.
Comment 6 Jason Merrill 2011-11-07 04:40:25 UTC
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
Comment 7 vincenzo Innocente 2011-11-07 09:23:30 UTC
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;

}
Comment 8 vincenzo Innocente 2011-11-07 09:38:08 UTC
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()
Comment 9 Jason Merrill 2011-11-07 17:51:43 UTC
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
Comment 10 Jason Merrill 2011-11-07 21:17:28 UTC
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.
Comment 11 Jason Merrill 2011-11-07 21:17:48 UTC
Fixed for 4.7.
Comment 12 vincenzo Innocente 2011-11-08 08:49:22 UTC
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
Comment 13 André Wöbbeking 2012-12-08 12:09:37 UTC
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é
Comment 14 André Wöbbeking 2013-02-10 15:50:14 UTC
Could anyone reply to my last question? Thanks in advance.
Comment 15 Jason Merrill 2013-02-11 15:44:51 UTC
(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.