diff options
-rw-r--r-- | gcc/cp/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/cp/name-lookup.c | 45 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/lookup/friend11.C | 24 |
4 files changed, 79 insertions, 2 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5141298..57c1e41 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2007-07-28 Simon Martin <simartin@users.sourceforge.net> + Mark Mitchell <mark@codesourcery.com> + + PR c++/30917 + * name-lookup.c (lookup_name_real): Non namespace-scope bindings can be + hidden due to friend declarations in local classes. + 2007-07-27 Douglas Gregor <doug.gregor@gmail.com> * typeck.c (structural_comptypes): Compare DECLTYPE_TYPE nodes. diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 0687258..8146638 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -3990,8 +3990,49 @@ lookup_name_real (tree name, int prefer_type, int nonclass, bool block_p, if (binding) { - /* Only namespace-scope bindings can be hidden. */ - gcc_assert (!hidden_name_p (binding)); + if (hidden_name_p (binding)) + { + /* A non namespace-scope binding can only be hidden if + we are in a local class, due to friend declarations. + In particular, consider: + + void f() { + struct A { + friend struct B; + void g() { B* b; } // error: B is hidden + } + struct B {}; + } + + The standard says that "B" is a local class in "f" + (but not nested within "A") -- but that name lookup + for "B" does not find this declaration until it is + declared directly with "f". + + In particular: + + [class.friend] + + If a friend declaration appears in a local class and + the name specified is an unqualified name, a prior + declaration is looked up without considering scopes + that are outside the innermost enclosing non-class + scope. For a friend class declaration, if there is no + prior declaration, the class that is specified + belongs to the innermost enclosing non-class scope, + but if it is subsequently referenced, its name is not + found by name lookup until a matching declaration is + provided in the innermost enclosing nonclass scope. + */ + gcc_assert (current_class_type && + LOCAL_CLASS_P (current_class_type)); + + /* This binding comes from a friend declaration in the local + class. The standard (11.4.8) states that the lookup can + only succeed if there is a non-hidden declaration in the + current scope, which is not the case here. */ + POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE); + } val = binding; break; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5352bce..52e0b5e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-07-28 Simon Martin <simartin@users.sourceforge.net> + + PR c++/30917 + * g++.dg/lookup/friend11.C: New test. + 2007-07-28 Daniel Franke <franke.daniel@gmail.com> * gfortran.dg/namelist_5.f90: Adjusted error message. diff --git a/gcc/testsuite/g++.dg/lookup/friend11.C b/gcc/testsuite/g++.dg/lookup/friend11.C new file mode 100644 index 0000000..ab8a9e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/friend11.C @@ -0,0 +1,24 @@ +/* PR c++/30917 */ +/* This used to ICE */ +/* { dg-do "compile" } */ + + +// This is invalid: QGList must only be looked up in count. +class QGList; +unsigned count() { + class QGListIterator { + friend class QGList; + QGListIterator( const QGList & ); /* { dg-error "expected|with no type" } */ + }; + return 0; +} + +// This is valid. +unsigned count2() { + class QGList2; + class QGListIterator2 { + friend class QGList2; + QGListIterator2( const QGList2 & ); + }; + return 0; +} |