diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 8acfe55b86ba5499ab7b7aa7d08a41f682491a21..e79b20e559267c2586c6ac700d5c18cf41bce0e2 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@
+2004-11-25  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
+
+	Friend class name lookup 3/n, PR c++/3332
+	* name-lookup.c (push_inner_scope, pop_inner_scope): New functions.
+	(lookup_type_scope): Don't deal with name from user declaration
+	specially.
+	* name-lookup.h (push_inner_scope, pop_inner_scope): Add declarations.
+	* parser.c (cp_parser_class_specifier): Use push_inner_scope and
+	pop_inner_scope.
+
 2004-11-25  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
 
 	Friend class name lookup 2/n, PR c++/14513, c++/15410
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index a0188620759b21d6f32b929863aa98c95a43bd35..aa5e5d24454ac57718b876225eb96c8d736ab718 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -2542,8 +2542,10 @@ is_ancestor (tree root, tree child)
     }
 }
 
-/* Enter the class or namespace scope indicated by T.  Returns TRUE iff
-   pop_scope should be called later to exit this scope.  */
+/* Enter the class or namespace scope indicated by T suitable for
+   name lookup.  T can be arbitrary scope, not necessary nested inside
+   the current scope.  Returns TRUE iff pop_scope should be called
+   later to exit this scope.  */
 
 bool
 push_scope (tree t)
@@ -2578,6 +2580,110 @@ pop_scope (tree t)
   else if CLASS_TYPE_P (t)
     pop_nested_class ();
 }
+
+/* Subroutine of push_inner_scope.  */
+
+static void
+push_inner_scope_r (tree outer, tree inner)
+{
+  tree prev;
+
+  if (outer == inner
+      || (TREE_CODE (inner) != NAMESPACE_DECL && !CLASS_TYPE_P (inner)))
+    return;
+
+  prev = CP_DECL_CONTEXT (TREE_CODE (inner) == NAMESPACE_DECL ? inner : TYPE_NAME (inner));
+  if (outer != prev)
+    push_inner_scope_r (outer, prev);
+  if (TREE_CODE (inner) == NAMESPACE_DECL)
+    {
+      struct cp_binding_level *save_template_parm = 0;
+      /* Temporary take out template parameter scopes.  They are saved
+	 in reversed order in save_template_parm.  */
+      while (current_binding_level->kind == sk_template_parms)
+	{
+	  struct cp_binding_level *b = current_binding_level;
+	  current_binding_level = b->level_chain;
+	  b->level_chain = save_template_parm;
+	  save_template_parm = b;
+	}
+
+      resume_scope (NAMESPACE_LEVEL (inner));
+      current_namespace = inner;
+
+      /* Restore template parameter scopes.  */
+      while (save_template_parm)
+	{
+	  struct cp_binding_level *b = save_template_parm;
+	  save_template_parm = b->level_chain;
+	  b->level_chain = current_binding_level;
+	  current_binding_level = b;
+	}
+    }
+  else
+    pushclass (inner);
+}
+
+/* Enter the scope INNER from current scope.  INNER must be a scope
+   nested inside current scope.  This works with both name lookup and
+   pushing name into scope.  In case a template parameter scope is present,
+   namespace is pushed under the template parameter scope according to
+   name lookup rule in 14.6.1/6.
+   
+   Return the former current scope suitable for pop_inner_scope.  */
+
+tree
+push_inner_scope (tree inner)
+{
+  tree outer = current_scope ();
+  if (!outer)
+    outer = current_namespace;
+
+  push_inner_scope_r (outer, inner);
+  return outer;
+}
+
+/* Exit the current scope INNER back to scope OUTER.  */
+
+void
+pop_inner_scope (tree outer, tree inner)
+{
+  if (outer == inner
+      || (TREE_CODE (inner) != NAMESPACE_DECL && !CLASS_TYPE_P (inner)))
+    return;
+
+  while (outer != inner)
+    {
+      if (TREE_CODE (inner) == NAMESPACE_DECL)
+	{
+	  struct cp_binding_level *save_template_parm = 0;
+	  /* Temporary take out template parameter scopes.  They are saved
+	     in reversed order in save_template_parm.  */
+	  while (current_binding_level->kind == sk_template_parms)
+	    {
+	      struct cp_binding_level *b = current_binding_level;
+	      current_binding_level = b->level_chain;
+	      b->level_chain = save_template_parm;
+	      save_template_parm = b;
+	    }
+
+	  pop_namespace ();
+
+	  /* Restore template parameter scopes.  */
+	  while (save_template_parm)
+	    {
+	      struct cp_binding_level *b = save_template_parm;
+	      save_template_parm = b->level_chain;
+	      b->level_chain = current_binding_level;
+	      current_binding_level = b;
+	    }
+	}
+      else
+	popclass ();
+
+      inner = CP_DECL_CONTEXT (TREE_CODE (inner) == NAMESPACE_DECL ? inner : TYPE_NAME (inner));
+    }
+}
 
 /* Do a pushlevel for class declarations.  */
 
@@ -4125,14 +4231,17 @@ lookup_name (tree name, int prefer_type)
 
 /* Look up NAME for type used in elaborated name specifier in
    the scopes given by SCOPE.  SCOPE can be either TS_CURRENT or
-   TS_WITHIN_ENCLOSING_NON_CLASS (possibly more scope is checked if 
-   cleanup or template parameter scope is encountered).
+   TS_WITHIN_ENCLOSING_NON_CLASS.  Although not implied by the
+   name, more scopes are checked if cleanup or template parameter
+   scope is encountered.
 
    Unlike lookup_name_real, we make sure that NAME is actually
-   declared in the desired scope, not from inheritance, using 
-   declaration, nor using directive.  A TYPE_DECL best matching
-   the NAME is returned.  Catching error and issuing diagnostics
-   are caller's responsibility.  */
+   declared in the desired scope, not from inheritance, nor using
+   directive.  For using declaration, there is DR138 still waiting
+   to be resolved.
+
+   A TYPE_DECL best matching the NAME is returned.  Catching error
+   and issuing diagnostics are caller's responsibility.  */
 
 tree
 lookup_type_scope (tree name, tag_scope scope)
@@ -4182,12 +4291,9 @@ lookup_type_scope (tree name, tag_scope scope)
 	  /* If this is the kind of thing we're looking for, we're done.
 	     Ignore names found via using declaration.  See DR138 for
 	     current status.  */
-	  if (qualify_lookup (iter->type, LOOKUP_PREFER_TYPES)
-	      && (CP_DECL_CONTEXT (iter->type) == iter->scope->this_entity))
+	  if (qualify_lookup (iter->type, LOOKUP_PREFER_TYPES))
 	    val = iter->type;
-	  else if (qualify_lookup (iter->value, LOOKUP_PREFER_TYPES)
-		   && (CP_DECL_CONTEXT (iter->value)
-		       == iter->scope->this_entity))
+	  else if (qualify_lookup (iter->value, LOOKUP_PREFER_TYPES))
 	    val = iter->value;
 	}
 	
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 08a9ba504b9e99307832a0841add3ba61934a971..011c62a6f136bd5dc4c27971a936b7f3458e046e 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -306,6 +306,8 @@ extern void keep_next_level (bool);
 extern bool is_ancestor (tree, tree);
 extern bool push_scope (tree);
 extern void pop_scope (tree);
+extern tree push_inner_scope (tree);
+extern void pop_inner_scope (tree, tree);
 extern void push_binding_level (struct cp_binding_level *);
 
 extern void push_namespace (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 3548b45c117515e13b38f022c9aae6a9b77f874d..b2164b77235f527ab884c6223d56742a16025bad 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -12240,7 +12240,7 @@ cp_parser_class_specifier (cp_parser* parser)
   int has_trailing_semicolon;
   bool nested_name_specifier_p;
   unsigned saved_num_template_parameter_lists;
-  bool pop_p = false;
+  tree old_scope = NULL_TREE;
   tree scope = NULL_TREE;
 
   push_deferring_access_checks (dk_no_deferred);
@@ -12279,7 +12279,7 @@ cp_parser_class_specifier (cp_parser* parser)
   if (nested_name_specifier_p)
     {
       scope = CP_DECL_CONTEXT (TYPE_MAIN_DECL (type));
-      pop_p = push_scope (scope);
+      old_scope = push_inner_scope (scope);
     }
   type = begin_class_definition (type);
 
@@ -12304,8 +12304,8 @@ cp_parser_class_specifier (cp_parser* parser)
     }
   if (type != error_mark_node)
     type = finish_struct (type, attributes);
-  if (pop_p)
-    pop_scope (scope);
+  if (nested_name_specifier_p)
+    pop_inner_scope (old_scope, scope);
   /* If this class is not itself within the scope of another class,
      then we need to parse the bodies of all of the queued function
      definitions.  Note that the queued functions defined in a class
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e82c3fd17db67ab75710ca4811302ca9153536ae..934cb9aa81e8579b0952df6ca6f9526943ea3d72 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2004-11-25  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
+
+	Friend class name lookup 3/n, PR c++/3332
+	* g++.dg/lookup/friend3.C: New test.
+	* g++.dg/lookup/friend4.C: Likewise.
+	* g++.dg/lookup/friend5.C: Likewise.
+
 2004-11-25  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
 
 	Friend class name lookup 2/n, PR c++/14513, c++/15410
diff --git a/gcc/testsuite/g++.dg/lookup/friend3.C b/gcc/testsuite/g++.dg/lookup/friend3.C
new file mode 100644
index 0000000000000000000000000000000000000000..2a58dd8ca057cfd324ec808fbea524eb739b152d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/friend3.C
@@ -0,0 +1,19 @@
+// Copyright (C) 2004 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+// Friend name lookup in class defined outside its namespace
+
+namespace A {
+  class B;
+  class C;
+}
+
+class A::B {
+  friend class C;
+  typedef int i;
+};
+
+class A::C {
+  A::B::i j;
+};
diff --git a/gcc/testsuite/g++.dg/lookup/friend4.C b/gcc/testsuite/g++.dg/lookup/friend4.C
new file mode 100644
index 0000000000000000000000000000000000000000..86ac9156109b65b8a3b2929c8d89158d131ea76c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/friend4.C
@@ -0,0 +1,22 @@
+// Copyright (C) 2004 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+// Friend name lookup in class defined outside its namespace
+// (Local class case)
+
+void f() {
+  class A {
+    class B;
+    class C;
+  };
+
+  class A::B {
+    friend class C;
+    typedef int i;
+  };
+
+  class A::C {
+    A::B::i j;
+  };
+}
diff --git a/gcc/testsuite/g++.dg/lookup/friend5.C b/gcc/testsuite/g++.dg/lookup/friend5.C
new file mode 100644
index 0000000000000000000000000000000000000000..5be37bbd5abc2dbe549a21a72be6ae1ac3846e63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/friend5.C
@@ -0,0 +1,34 @@
+// { dg-do compile }
+
+// Origin: aroach@stoic.electriceyeball.com
+
+// PR c++/3332: Friend function lookup in class defined outside its
+// namespace
+
+namespace N
+{
+  class A;
+}
+
+class N::A
+{
+  void x();
+  friend void func(void);
+};
+
+namespace N
+{
+  void func(void);
+}
+
+void N::func(void)
+{
+  N::A a;
+  a.x();
+}
+
+int main()
+{
+  return 0;
+}
+