aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2008-01-22 14:50:37 -0500
committerJason Merrill <jason@gcc.gnu.org>2008-01-22 14:50:37 -0500
commit7655e009c8f506454a5cc16bace7282b67513c00 (patch)
treed611d9e565111bcaa97ac42d5cbee302da3e523c
parentb5ca4fd2f6927b6c2cc9ca3f315980b9cbf45605 (diff)
downloadgcc-7655e009c8f506454a5cc16bace7282b67513c00.zip
gcc-7655e009c8f506454a5cc16bace7282b67513c00.tar.gz
gcc-7655e009c8f506454a5cc16bace7282b67513c00.tar.bz2
re PR c++/34912 (ICE with friend in local class)
PR c++/34912 * friend.c (do_friend): Check for prior declaration of a friend function of a local class. * name-lookup.c (lookup_name_innermost_nonclass_level): No longer static. * name-lookup.h: Declare it. From-SVN: r131740
-rw-r--r--gcc/cp/ChangeLog9
-rw-r--r--gcc/cp/friend.c24
-rw-r--r--gcc/cp/name-lookup.c3
-rw-r--r--gcc/cp/name-lookup.h1
-rw-r--r--gcc/testsuite/g++.dg/lookup/friend12.C10
-rw-r--r--gcc/testsuite/g++.dg/parse/local-class1.C2
-rw-r--r--gcc/testsuite/g++.old-deja/g++.jason/scoping12.C2
7 files changed, 42 insertions, 9 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 235f7cb..45297de 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,12 @@
+2008-01-22 Jason Merrill <jason@redhat.com>
+
+ PR c++/34912
+ * friend.c (do_friend): Check for prior declaration of a friend
+ function of a local class.
+ * name-lookup.c (lookup_name_innermost_nonclass_level):
+ No longer static.
+ * name-lookup.h: Declare it.
+
2008-01-22 Tom Tromey <tromey@redhat.com>
PR c++/34829:
diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c
index 094501b1..ffb0baa 100644
--- a/gcc/cp/friend.c
+++ b/gcc/cp/friend.c
@@ -526,11 +526,25 @@ do_friend (tree ctype, tree declarator, tree decl,
is instantiated. */
decl = push_template_decl_real (decl, /*is_friend=*/true);
else if (current_function_decl)
- /* This must be a local class, so pushdecl will be ok, and
- insert an unqualified friend into the local scope
- (rather than the containing namespace scope, which the
- next choice will do). */
- decl = pushdecl_maybe_friend (decl, /*is_friend=*/true);
+ {
+ /* This must be a local class. 11.5p11:
+
+ If a friend declaration appears in a local class (9.8) 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 function declaration, if there is no prior
+ declaration, the program is ill-formed. */
+ tree t = lookup_name_innermost_nonclass_level (DECL_NAME (decl));
+ if (t)
+ decl = pushdecl_maybe_friend (decl, /*is_friend=*/true);
+ else
+ {
+ error ("friend declaration %qD in local class without "
+ "prior declaration", decl);
+ return error_mark_node;
+ }
+ }
else
{
/* We can't use pushdecl, as we might be in a template
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index c21940c..ded1d2e 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -43,7 +43,6 @@ struct scope_binding {
static cxx_scope *innermost_nonclass_level (void);
static cxx_binding *binding_for_name (cxx_scope *, tree);
-static tree lookup_name_innermost_nonclass_level (tree);
static tree push_overloaded_decl (tree, int, bool);
static bool lookup_using_namespace (tree, struct scope_binding *, tree,
tree, int);
@@ -4202,7 +4201,7 @@ lookup_type_scope (tree name, tag_scope scope)
/* Similar to `lookup_name' but look only in the innermost non-class
binding level. */
-static tree
+tree
lookup_name_innermost_nonclass_level (tree name)
{
struct cp_binding_level *b;
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 7da57be..a4c057e 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -319,6 +319,7 @@ extern bool hidden_name_p (tree);
extern tree remove_hidden_names (tree);
extern tree lookup_qualified_name (tree, tree, bool, bool);
extern tree lookup_name_nonclass (tree);
+extern tree lookup_name_innermost_nonclass_level (tree);
extern tree lookup_function_nonclass (tree, tree, bool);
extern void push_local_binding (tree, tree, int);
extern bool pushdecl_class_level (tree);
diff --git a/gcc/testsuite/g++.dg/lookup/friend12.C b/gcc/testsuite/g++.dg/lookup/friend12.C
new file mode 100644
index 0000000..95cfd5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/friend12.C
@@ -0,0 +1,10 @@
+// PR c++/34912
+
+void foo()
+{
+ struct A
+ {
+ friend void bar(); // { dg-error "without prior declaration" }
+ };
+ bar(); // { dg-error "not declared" }
+}
diff --git a/gcc/testsuite/g++.dg/parse/local-class1.C b/gcc/testsuite/g++.dg/parse/local-class1.C
index 4fc7e6e..518dd7a 100644
--- a/gcc/testsuite/g++.dg/parse/local-class1.C
+++ b/gcc/testsuite/g++.dg/parse/local-class1.C
@@ -9,6 +9,6 @@ void f ()
{
class c
{
- friend void g () { } // { dg-error "local class definition" "" }
+ friend void g () { } // { dg-error "local class" "" }
};
}
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/scoping12.C b/gcc/testsuite/g++.old-deja/g++.jason/scoping12.C
index 3a1060a..a783416 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/scoping12.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/scoping12.C
@@ -2,7 +2,7 @@
void f ()
{
struct A {
- friend void g ();
+ friend void g (); // { dg-error "without prior declaration" }
};
}
void h () {