GCC Bugzilla – Bug 13495
Friendship to class nested within a template is broken
Last modified: 2004-10-20 16:25:46 UTC
Hi. I don´t exactly know what the correct syntax to do this is, since I couldn´t look up in the specification, but it looks like a bug because a) I couldn´t figure out a way to do this with GCC b) I could figure out a way which does work with MSVC but doesn´t with GCC so here it is: a template class which contains a nested class which itself is no template but inherits the template parameter: template<typename T> class A{ public: class B{}; }; class A<T>::B should be friend of another class: template<typename T> class OtherClass{ XXX }; A<T>::B is a dependent type, so using it requires the 'typename'-keyword. ("implicit typename deprecated") a) XXX == friend typename A<T>::B; doesn´t work because it requires the 'class'-keyword because it´s a class friendship b) XXX == friend class A<T>::B; doesn´t work because A<T>::B is a dependent typename, which requires the 'typename' keyword. c) XXX == friend typename class A<T>::B; or d) XXX == friend class typename A<T>::B pass out with parse error. MSVC compiles a) (wrongly) and c) (possibly correct) Thank you, Stefan
It's been clarified by DR180 that the form: friend class A<T>::B; is the only allowed. In fact, the name is implicitily a type because of the 'class' keyword put in front of it, so a typename is not needed (and it is not allowed by syntax either). This has been correctly implemented by the new parser in GCC 3.4. Then you must understand the difference between: template<class T> class OtherClass { friend class A<T>::B; }; and: template<class T> class OtherClass { template <class Q> friend class A<Q>::B; }; In the first case, you're saying that, for each OtherClass<X>, A<X>::B is a friend (only for the same X!). In the second, you're saying that for each OtherClass<X>, any A<Y>::B is a friend (for any Y). Nonetheless, there is indeed a bug in GCC. I propose this testcase: -------------------------------------------- template<typename T> class A{ public: class B { void func1(void); void func2(void); }; }; template<typename Q> class F1 { friend class A<Q>::B; enum { foo = 0 }; }; template<typename Q> class F2 { template<typename T> friend class A<T>::B; enum { foo = 0 }; }; template <typename T> void A<T>::B::func1(void) { (void)F1<T>::foo; // OK, A<T>::B is a friend for this T (#1) (void)F2<T>::foo; // OK, any A<K>::B is a friend (#2) } template <typename T> void A<T>::B::func2(void) { (void)F1<double>::foo; // ERROR, A<double>::T is not a friend (#3) (void)F2<double>::foo; // OK, any A<K>::B is a friend (#4) } template class A<void>; -------------------------------------------- Thus, GCC should emit an error only for #3. Instead it emits an error for #2 and not for #3. This is clearly a bug (but not a regression, since the previous versions of GCC were even more broken wrt friendships to nested classes or specializations of templates). This is a job for Kriang :)
Created attachment 6895 [details] Patch that gets `template <class T> friend class A<T>::B;' to be accepted In case there's interest, this patch (written for 3.2.3; might still apply to 3.3) is implemented to enable the compiler to parse `template <class T> friend class A<T>::B;' correctly. So far I've only verified the fix on 3.2.3, and I haven't tested it any further than that, but if there's interest in integrating a fix for this problem in GCC 3.3, feel free to pick the patch up from this point.
Kriang, it looks like g++.old-deja/g++.pt/friend44.C is related to this bug. The testcase is xfailed on mainline, and you already fixed half of it with your patch for PR5369. I guess your patch for this bug also cures that testcase?
Yes, the patch will also fix the warning produced when testing g++.old-deja/g++.pt/friend44.C.
Patch in progress. Note that even with the fix, GCC still doesn't diagnose the code in Comment #1 correctly because of PR16617.
Patch submitted: http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00557.html
Subject: Bug 13495 CVSROOT: /cvs/gcc Module name: gcc Changes by: lerdsuwa@gcc.gnu.org 2004-10-20 16:20:51 Modified files: gcc/cp : ChangeLog cp-tree.h decl.c friend.c parser.c pt.c gcc/testsuite : ChangeLog gcc/testsuite/g++.old-deja/g++.pt: friend44.C Added files: gcc/testsuite/g++.dg/template: memfriend9.C memfriend10.C memfriend11.C memfriend12.C memfriend13.C memfriend14.C memfriend15.C memfriend16.C memfriend17.C Log message: PR c++/13495 * decl.c (make_unbound_class_template): Add PARM_LIST parameter. * cp-tree.h (make_unbound_class_template): Adjust prototype. * parser.c (cp_parser_lookup_name): Adjust call to make_unbound_class_template. (cp_parser_single_declaration): Handle member class of class template as template friend parsing correctly. * friend.c (is_friend): Call is_specialization_of_friend for template friend class. (make_friend_class): Handle member class of class template as template friend. * pt.c (is_specialization_of_friend): Likewise. (instantiate_class_template): Likewise. (tsubst): Adjust call to make_unbound_class_template. * g++.dg/template/memfriend9.C: New test. * g++.dg/template/memfriend10.C: Likewise. * g++.dg/template/memfriend11.C: Likewise. * g++.dg/template/memfriend12.C: Likewise. * g++.dg/template/memfriend13.C: Likewise. * g++.dg/template/memfriend14.C: Likewise. * g++.dg/template/memfriend15.C: Likewise. * g++.dg/template/memfriend16.C: Likewise. * g++.dg/template/memfriend17.C: Likewise. * g++.old-deja/g++.pt/friend44.C: Remove bogus error. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&r1=1.4447&r2=1.4448 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/cp-tree.h.diff?cvsroot=gcc&r1=1.1065&r2=1.1066 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/decl.c.diff?cvsroot=gcc&r1=1.1316&r2=1.1317 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/friend.c.diff?cvsroot=gcc&r1=1.101&r2=1.102 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/parser.c.diff?cvsroot=gcc&r1=1.267&r2=1.268 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/pt.c.diff?cvsroot=gcc&r1=1.936&r2=1.937 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&r1=1.4478&r2=1.4479 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend9.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend10.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend11.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend12.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend13.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend14.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend15.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend16.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/memfriend17.C.diff?cvsroot=gcc&r1=NONE&r2=1.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.old-deja/g++.pt/friend44.C.diff?cvsroot=gcc&r1=1.3&r2=1.4
I am closing this as fixed by patch: http://gcc.gnu.org/ml/gcc-patches/2004-10/msg01709.html For access checking problem mentioned in the comments, it's already covered in PR16617. That part requires some structural changes that won't be investigated for some time.