OSDN Git Service

* call.c (struct z_candidate): Add explicit_targs field.
[pf3gnuchains/gcc-fork.git] / gcc / cp / friend.c
index 30b3c51..03748fe 100644 (file)
 /* Help friends in C++.
-   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+   2007, 2008  Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
+GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "tree.h"
 #include "rtl.h"
+#include "expr.h"
 #include "cp-tree.h"
 #include "flags.h"
 #include "output.h"
 #include "toplev.h"
 
-static void add_friend PROTO((tree, tree));
-static void add_friends PROTO((tree, tree, tree));
-
 /* Friend data structures are described in cp-tree.h.  */
 
-/* Returns non-zero if SUPPLICANT is a friend of TYPE.  */
+/* Returns nonzero if SUPPLICANT is a friend of TYPE.  */
 
 int
-is_friend (type, supplicant)
-     tree type, supplicant;
+is_friend (tree type, tree supplicant)
 {
   int declp;
-  register tree list;
+  tree list;
   tree context;
 
   if (supplicant == NULL_TREE || type == NULL_TREE)
     return 0;
 
-  declp = (TREE_CODE_CLASS (TREE_CODE (supplicant)) == 'd');
+  declp = DECL_P (supplicant);
 
   if (declp)
     /* It's a function decl.  */
     {
       tree list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type));
       tree name = DECL_NAME (supplicant);
-      tree ctype;
-
-      if (DECL_FUNCTION_MEMBER_P (supplicant))
-       ctype = DECL_CLASS_CONTEXT (supplicant);
-      else
-       ctype = NULL_TREE;
 
       for (; list ; list = TREE_CHAIN (list))
        {
-         if (name == TREE_PURPOSE (list))
+         if (name == FRIEND_NAME (list))
            {
-             tree friends = TREE_VALUE (list);
+             tree friends = FRIEND_DECLS (list);
              for (; friends ; friends = TREE_CHAIN (friends))
                {
-                 if (same_type_p (ctype, TREE_PURPOSE (friends)))
-                   return 1;
+                 tree this_friend = TREE_VALUE (friends);
 
-                 if (TREE_VALUE (friends) == NULL_TREE)
+                 if (this_friend == NULL_TREE)
                    continue;
 
-                 if (supplicant == TREE_VALUE (friends))
+                 if (supplicant == this_friend)
                    return 1;
 
-                 /* With -fguiding-decls we are more lenient about
-                    friendship.  This is bogus in general since two
-                    specializations of a template with non-type
-                    template parameters may have the same type, but
-                    be different.  
-
-                    Temporarily, we are also more lenient to deal
-                    with nested friend functions, for which there can
-                    be more than one FUNCTION_DECL, despite being the
-                    same function.  When that's fixed, the
-                    FUNCTION_MEMBER_P bit can go.  */
-                 if ((flag_guiding_decls 
-                      || DECL_FUNCTION_MEMBER_P (supplicant))
-                     && same_type_p (TREE_TYPE (supplicant),
-                                     TREE_TYPE (TREE_VALUE (friends))))
-                   return 1;
-
-                 if (TREE_CODE (TREE_VALUE (friends)) == TEMPLATE_DECL
-                     && is_specialization_of (supplicant, 
-                                              TREE_VALUE (friends)))
+                 if (is_specialization_of_friend (supplicant, this_friend))
                    return 1;
                }
              break;
@@ -104,30 +77,40 @@ is_friend (type, supplicant)
   else
     /* It's a type.  */
     {
-      if (type == supplicant)
+      if (same_type_p (supplicant, type))
        return 1;
-      
+
       list = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (TYPE_MAIN_DECL (type)));
       for (; list ; list = TREE_CHAIN (list))
        {
          tree t = TREE_VALUE (list);
 
-         if (TREE_CODE (t) == TEMPLATE_DECL ? 
-             is_specialization_of (TYPE_MAIN_DECL (supplicant), t) :
+         if (TREE_CODE (t) == TEMPLATE_DECL ?
+             is_specialization_of_friend (TYPE_MAIN_DECL (supplicant), t) :
              same_type_p (supplicant, t))
            return 1;
        }
-    }      
+    }
 
-  if (declp && DECL_FUNCTION_MEMBER_P (supplicant))
-    context = DECL_CLASS_CONTEXT (supplicant);
-  else if (! declp)
-    /* Local classes have the same access as the enclosing function.  */
-    context = hack_decl_function_context (TYPE_MAIN_DECL (supplicant));
+  if (declp)
+    {
+      if (DECL_FUNCTION_MEMBER_P (supplicant))
+       context = DECL_CONTEXT (supplicant);
+      else
+       context = NULL_TREE;
+    }
   else
-    context = NULL_TREE;
+    {
+      if (TYPE_CLASS_SCOPE_P (supplicant))
+       /* Nested classes get the same access as their enclosing types, as
+          per DR 45 (this is a change from the standard).  */
+       context = TYPE_CONTEXT (supplicant);
+      else
+       /* Local classes have the same access as the enclosing function.  */
+       context = decl_function_context (TYPE_MAIN_DECL (supplicant));
+    }
 
-  /* A namespace is not friend to anybody. */
+  /* A namespace is not friend to anybody.  */
   if (context && TREE_CODE (context) == NAMESPACE_DECL)
     context = NULL_TREE;
 
@@ -138,101 +121,66 @@ is_friend (type, supplicant)
 }
 
 /* Add a new friend to the friends of the aggregate type TYPE.
-   DECL is the FUNCTION_DECL of the friend being added.  */
+   DECL is the FUNCTION_DECL of the friend being added.
 
-static void
-add_friend (type, decl)
-     tree type, decl;
+   If COMPLAIN is true, warning about duplicate friend is issued.
+   We want to have this diagnostics during parsing but not
+   when a template is being instantiated.  */
+
+void
+add_friend (tree type, tree decl, bool complain)
 {
-  tree typedecl = TYPE_MAIN_DECL (type);
-  tree list = DECL_FRIENDLIST (typedecl);
-  tree name = DECL_NAME (decl);
+  tree typedecl;
+  tree list;
+  tree name;
+  tree ctx;
+
+  if (decl == error_mark_node)
+    return;
+
+  typedecl = TYPE_MAIN_DECL (type);
+  list = DECL_FRIENDLIST (typedecl);
+  name = DECL_NAME (decl);
+  type = TREE_TYPE (typedecl);
 
   while (list)
     {
-      if (name == TREE_PURPOSE (list))
+      if (name == FRIEND_NAME (list))
        {
-         tree friends = TREE_VALUE (list);
+         tree friends = FRIEND_DECLS (list);
          for (; friends ; friends = TREE_CHAIN (friends))
            {
              if (decl == TREE_VALUE (friends))
                {
-                 cp_warning ("`%D' is already a friend of class `%T'",
-                             decl, type);
-                 cp_warning_at ("previous friend declaration of `%D'",
-                                TREE_VALUE (friends));
+                 if (complain)
+                   warning (0, "%qD is already a friend of class %qT",
+                            decl, type);
                  return;
                }
            }
-         TREE_VALUE (list) = tree_cons (error_mark_node, decl,
+
+         maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
+
+         TREE_VALUE (list) = tree_cons (NULL_TREE, decl,
                                         TREE_VALUE (list));
          return;
        }
       list = TREE_CHAIN (list);
     }
-  DECL_FRIENDLIST (typedecl)
-    = tree_cons (DECL_NAME (decl), build_tree_list (error_mark_node, decl),
-                DECL_FRIENDLIST (typedecl));
-  if (DECL_NAME (decl) == ansi_opname[(int) MODIFY_EXPR])
-    {
-      tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
-      TYPE_HAS_ASSIGNMENT (TREE_TYPE (typedecl)) = 1;
-      if (parmtypes && TREE_CHAIN (parmtypes))
-       {
-         tree parmtype = TREE_VALUE (TREE_CHAIN (parmtypes));
-         if (TREE_CODE (parmtype) == REFERENCE_TYPE
-             && TREE_TYPE (parmtypes) == TREE_TYPE (typedecl))
-           TYPE_HAS_ASSIGN_REF (TREE_TYPE (typedecl)) = 1;
-       }
-    }
-}
 
-/* Declare that every member function NAME in FRIEND_TYPE
-   (which may be NULL_TREE) is a friend of type TYPE.  */
+  ctx = DECL_CONTEXT (decl);
+  if (ctx && CLASS_TYPE_P (ctx) && !uses_template_parms (ctx))
+    perform_or_defer_access_check (TYPE_BINFO (ctx), decl, decl);
 
-static void
-add_friends (type, name, friend_type)
-     tree type, name, friend_type;
-{
-  tree typedecl = TYPE_MAIN_DECL (type);
-  tree list = DECL_FRIENDLIST (typedecl);
+  maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
 
-  while (list)
-    {
-      if (name == TREE_PURPOSE (list))
-       {
-         tree friends = TREE_VALUE (list);
-         while (friends && TREE_PURPOSE (friends) != friend_type)
-           friends = TREE_CHAIN (friends);
-         if (friends)
-           {
-             if (friend_type)
-               warning ("method `%s::%s' is already a friend of class",
-                        TYPE_NAME_STRING (friend_type),
-                        IDENTIFIER_POINTER (name));
-             else
-               warning ("function `%s' is already a friend of class `%s'",
-                        IDENTIFIER_POINTER (name),
-                        IDENTIFIER_POINTER (DECL_NAME (typedecl)));
-           }
-         else
-           TREE_VALUE (list) = tree_cons (friend_type, NULL_TREE,
-                                          TREE_VALUE (list));
-         return;
-       }
-      list = TREE_CHAIN (list);
-    }
   DECL_FRIENDLIST (typedecl)
-    = tree_cons (name,
-                build_tree_list (friend_type, NULL_TREE),
+    = tree_cons (DECL_NAME (decl), build_tree_list (NULL_TREE, decl),
                 DECL_FRIENDLIST (typedecl));
-  if (! strncmp (IDENTIFIER_POINTER (name),
-                IDENTIFIER_POINTER (ansi_opname[(int) MODIFY_EXPR]),
-                strlen (IDENTIFIER_POINTER (ansi_opname[(int) MODIFY_EXPR]))))
-    {
-      TYPE_HAS_ASSIGNMENT (TREE_TYPE (typedecl)) = 1;
-      sorry ("declaring \"friend operator =\" will not find \"operator = (X&)\" if it exists");
-    }
+  if (!uses_template_parms (type))
+    DECL_BEFRIENDING_CLASSES (decl)
+      = tree_cons (NULL_TREE, type,
+                  DECL_BEFRIENDING_CLASSES (decl));
 }
 
 /* Make FRIEND_TYPE a friend class to TYPE.  If FRIEND_TYPE has already
@@ -243,251 +191,405 @@ add_friends (type, name, friend_type)
    classes that are not defined.  If a type has not yet been defined,
    then the DECL_WAITING_FRIENDS contains a list of types
    waiting to make it their friend.  Note that these two can both
-   be in use at the same time!  */
+   be in use at the same time!
+
+   If COMPLAIN is true, warning about duplicate friend is issued.
+   We want to have this diagnostics during parsing but not
+   when a template is being instantiated.  */
 
 void
-make_friend_class (type, friend_type)
-     tree type, friend_type;
+make_friend_class (tree type, tree friend_type, bool complain)
 {
   tree classes;
-  int is_template_friend;
 
-  if (IS_SIGNATURE (type))
-    {
-      error ("`friend' declaration in signature definition");
-      return;
-    }
-  if (IS_SIGNATURE (friend_type) || ! IS_AGGR_TYPE (friend_type))
-    {
-      cp_error ("invalid type `%T' declared `friend'", friend_type);
-      return;
-    }
+  /* CLASS_TEMPLATE_DEPTH counts the number of template headers for
+     the enclosing class.  FRIEND_DEPTH counts the number of template
+     headers used for this friend declaration.  TEMPLATE_MEMBER_P,
+     defined inside the `if' block for TYPENAME_TYPE case, is true if
+     a template header in FRIEND_DEPTH is intended for DECLARATOR.
+     For example, the code
+
+       template <class T> struct A {
+        template <class U> struct B {
+          template <class V> template <class W>
+            friend class C<V>::D;
+        };
+       };
+
+     will eventually give the following results
 
-  if (CLASS_TYPE_P (friend_type)
-      && CLASSTYPE_TEMPLATE_SPECIALIZATION (friend_type)
-      && uses_template_parms (friend_type))
+     1. CLASS_TEMPLATE_DEPTH equals 2 (for `T' and `U').
+     2. FRIEND_DEPTH equals 2 (for `V' and `W').
+     3. TEMPLATE_MEMBER_P is true (for `W').
+
+     The friend is a template friend iff FRIEND_DEPTH is nonzero.  */
+
+  int class_template_depth = template_class_depth (type);
+  int friend_depth = processing_template_decl - class_template_depth;
+
+  if (! MAYBE_CLASS_TYPE_P (friend_type))
     {
-      /* [temp.friend]
-        
-        Friend declarations shall not declare partial
-        specializations.  */
-      cp_error ("partial specialization `%T' declared `friend'",
-               friend_type);
+      error ("invalid type %qT declared %<friend%>", friend_type);
       return;
     }
 
-  if (processing_template_decl > template_class_depth (type))
+  if (friend_depth)
     /* If the TYPE is a template then it makes sense for it to be
        friends with itself; this means that each instantiation is
        friends with all other instantiations.  */
-    is_template_friend = 1;
+    {
+      if (CLASS_TYPE_P (friend_type)
+         && CLASSTYPE_TEMPLATE_SPECIALIZATION (friend_type)
+         && uses_template_parms (friend_type))
+       {
+         /* [temp.friend]
+            Friend declarations shall not declare partial
+            specializations.  */
+         error ("partial specialization %qT declared %<friend%>",
+                friend_type);
+         return;
+       }
+    }
   else if (same_type_p (type, friend_type))
     {
-      pedwarn ("class `%s' is implicitly friends with itself",
-              TYPE_NAME_STRING (type));
+      if (complain)
+       warning (0, "class %qT is implicitly friends with itself",
+                type);
       return;
     }
-  else
-    is_template_friend = 0;
 
-  GNU_xref_hier (type, friend_type, 0, 0, 1);
+  /* [temp.friend]
 
-  if (is_template_friend)
-    friend_type = CLASSTYPE_TI_TEMPLATE (friend_type);
+     A friend of a class or class template can be a function or
+     class template, a specialization of a function template or
+     class template, or an ordinary (nontemplate) function or
+     class.  */
+  if (!friend_depth)
+    ;/* ok */
+  else if (TREE_CODE (friend_type) == TYPENAME_TYPE)
+    {
+      if (TREE_CODE (TYPENAME_TYPE_FULLNAME (friend_type))
+         == TEMPLATE_ID_EXPR)
+       {
+         /* template <class U> friend class T::X<U>; */
+         /* [temp.friend]
+            Friend declarations shall not declare partial
+            specializations.  */
+         error ("partial specialization %qT declared %<friend%>",
+                friend_type);
+         return;
+       }
+      else
+       {
+         /* We will figure this out later.  */
+         bool template_member_p = false;
 
-  classes = CLASSTYPE_FRIEND_CLASSES (type);
-  while (classes 
-        /* Stop if we find the same type on the list.  */
-        && !(TREE_CODE (TREE_VALUE (classes)) == TEMPLATE_DECL ?
-             friend_type == TREE_VALUE (classes) :
-             same_type_p (TREE_VALUE (classes), friend_type)))
-    classes = TREE_CHAIN (classes);
-  if (classes) 
-    cp_warning ("`%T' is already a friend of `%T'",
-               TREE_VALUE (classes), type);
-  else
+         tree ctype = TYPE_CONTEXT (friend_type);
+         tree name = TYPE_IDENTIFIER (friend_type);
+         tree decl;
+
+         if (!uses_template_parms_level (ctype, class_template_depth
+                                                + friend_depth))
+           template_member_p = true;
+
+         if (class_template_depth)
+           {
+             /* We rely on tsubst_friend_class to check the
+                validity of the declaration later.  */
+             if (template_member_p)
+               friend_type
+                 = make_unbound_class_template (ctype,
+                                                name,
+                                                current_template_parms,
+                                                tf_error);
+             else
+               friend_type
+                 = make_typename_type (ctype, name, class_type, tf_error);
+           }
+         else
+           {
+             decl = lookup_member (ctype, name, 0, true);
+             if (!decl)
+               {
+                 error ("%qT is not a member of %qT", name, ctype);
+                 return;
+               }
+             if (template_member_p && !DECL_CLASS_TEMPLATE_P (decl))
+               {
+                 error ("%qT is not a member class template of %qT",
+                        name, ctype);
+                 error ("%q+D declared here", decl);
+                 return;
+               }
+             if (!template_member_p && (TREE_CODE (decl) != TYPE_DECL
+                                        || !CLASS_TYPE_P (TREE_TYPE (decl))))
+               {
+                 error ("%qT is not a nested class of %qT",
+                        name, ctype);
+                 error ("%q+D declared here", decl);
+                 return;
+               }
+
+             friend_type = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl));
+           }
+       }
+    }
+  else if (TREE_CODE (friend_type) == TEMPLATE_TYPE_PARM)
     {
-      CLASSTYPE_FRIEND_CLASSES (type)
-       = tree_cons (NULL_TREE, friend_type, CLASSTYPE_FRIEND_CLASSES (type));
+      /* template <class T> friend class T; */
+      error ("template parameter type %qT declared %<friend%>", friend_type);
+      return;
     }
-}
-
-/* Main friend processor.  This is large, and for modularity purposes,
-   has been removed from grokdeclarator.  It returns `void_type_node'
-   to indicate that something happened, though a FIELD_DECL is
-   not returned.
+  else if (!CLASSTYPE_TEMPLATE_INFO (friend_type))
+    {
+      /* template <class T> friend class A; where A is not a template */
+      error ("%q#T is not a template", friend_type);
+      return;
+    }
+  else
+    /* template <class T> friend class A; where A is a template */
+    friend_type = CLASSTYPE_TI_TEMPLATE (friend_type);
 
-   CTYPE is the class this friend belongs to.
+  if (friend_type == error_mark_node)
+    return;
 
-   DECLARATOR is the name of the friend.
+  /* See if it is already a friend.  */
+  for (classes = CLASSTYPE_FRIEND_CLASSES (type);
+       classes;
+       classes = TREE_CHAIN (classes))
+    {
+      tree probe = TREE_VALUE (classes);
 
-   DECL is the FUNCTION_DECL that the friend is.
+      if (TREE_CODE (friend_type) == TEMPLATE_DECL)
+       {
+         if (friend_type == probe)
+           {
+             if (complain)
+               warning (0, "%qD is already a friend of %qT", probe, type);
+             break;
+           }
+       }
+      else if (TREE_CODE (probe) != TEMPLATE_DECL)
+       {
+         if (same_type_p (probe, friend_type))
+           {
+             if (complain)
+               warning (0, "%qT is already a friend of %qT", probe, type);
+             break;
+           }
+       }
+    }
 
-   In case we are parsing a friend which is part of an inline
-   definition, we will need to store PARM_DECL chain that comes
-   with it into the DECL_ARGUMENTS slot of the FUNCTION_DECL.
+  if (!classes)
+    {
+      maybe_add_class_template_decl_list (type, friend_type, /*friend_p=*/1);
 
-   FLAGS is just used for `grokclassfn'.
+      CLASSTYPE_FRIEND_CLASSES (type)
+       = tree_cons (NULL_TREE, friend_type, CLASSTYPE_FRIEND_CLASSES (type));
+      if (TREE_CODE (friend_type) == TEMPLATE_DECL)
+       friend_type = TREE_TYPE (friend_type);
+      if (!uses_template_parms (type))
+       CLASSTYPE_BEFRIENDING_CLASSES (friend_type)
+         = tree_cons (NULL_TREE, type,
+                      CLASSTYPE_BEFRIENDING_CLASSES (friend_type));
+    }
+}
 
-   QUALS say what special qualifies should apply to the object
-   pointed to by `this'.  */
+/* Record DECL (a FUNCTION_DECL) as a friend of the
+   CURRENT_CLASS_TYPE.  If DECL is a member function, CTYPE is the
+   class of which it is a member, as named in the friend declaration.
+   DECLARATOR is the name of the friend.  FUNCDEF_FLAG is true if the
+   friend declaration is a definition of the function.  FLAGS is as
+   for grokclass fn.  */
 
 tree
-do_friend (ctype, declarator, decl, parmdecls, flags, quals, funcdef_flag)
-     tree ctype, declarator, decl, parmdecls;
-     enum overload_flags flags;
-     tree quals;
-     int funcdef_flag;
+do_friend (tree ctype, tree declarator, tree decl,
+          tree attrlist, enum overload_flags flags,
+          bool funcdef_flag)
 {
-  int is_friend_template = 0;
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+  gcc_assert (!ctype || MAYBE_CLASS_TYPE_P (ctype));
 
   /* Every decl that gets here is a friend of something.  */
   DECL_FRIEND_P (decl) = 1;
 
+  /* Unfortunately, we have to handle attributes here.  Normally we would
+     handle them in start_decl_1, but since this is a friend decl start_decl_1
+     never gets to see it.  */
+
+  /* Set attributes here so if duplicate decl, will have proper attributes.  */
+  cplus_decl_attributes (&decl, attrlist, 0);
+
   if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
     {
       declarator = TREE_OPERAND (declarator, 0);
-      if (TREE_CODE (declarator) == LOOKUP_EXPR)
-       declarator = TREE_OPERAND (declarator, 0);
       if (is_overloaded_fn (declarator))
        declarator = DECL_NAME (get_first_fn (declarator));
     }
 
-  if (TREE_CODE (decl) == FUNCTION_DECL)
-    is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();
-
   if (ctype)
     {
+      /* CLASS_TEMPLATE_DEPTH counts the number of template headers for
+        the enclosing class.  FRIEND_DEPTH counts the number of template
+        headers used for this friend declaration.  TEMPLATE_MEMBER_P is
+        true if a template header in FRIEND_DEPTH is intended for
+        DECLARATOR.  For example, the code
+
+          template <class T> struct A {
+            template <class U> struct B {
+              template <class V> template <class W>
+                friend void C<V>::f(W);
+            };
+          };
+
+        will eventually give the following results
+
+        1. CLASS_TEMPLATE_DEPTH equals 2 (for `T' and `U').
+        2. FRIEND_DEPTH equals 2 (for `V' and `W').
+        3. TEMPLATE_MEMBER_P is true (for `W').  */
+
+      int class_template_depth = template_class_depth (current_class_type);
+      int friend_depth = processing_template_decl - class_template_depth;
+      /* We will figure this out later.  */
+      bool template_member_p = false;
+
       tree cname = TYPE_NAME (ctype);
       if (TREE_CODE (cname) == TYPE_DECL)
        cname = DECL_NAME (cname);
 
       /* A method friend.  */
-      if (TREE_CODE (decl) == FUNCTION_DECL)
-       {
-         if (flags == NO_SPECIAL && ctype && declarator == cname)
-           DECL_CONSTRUCTOR_P (decl) = 1;
-
-         /* This will set up DECL_ARGUMENTS for us.  */
-         grokclassfn (ctype, decl, flags, quals);
-
-         if (is_friend_template)
-           decl = DECL_TI_TEMPLATE (push_template_decl (decl));
-         else if (template_class_depth (current_class_type))
-           decl = push_template_decl_real (decl, /*is_friend=*/1);
-
-         /* We can't do lookup in a type that involves template
-            parameters.  Instead, we rely on tsubst_friend_function
-            to check the validity of the declaration later.  */
-         if (uses_template_parms (ctype))
-           add_friend (current_class_type, decl);
-         /* A nested class may declare a member of an enclosing class
-            to be a friend, so we do lookup here even if CTYPE is in
-            the process of being defined.  */
-         else if (TYPE_SIZE (ctype) != 0 || TYPE_BEING_DEFINED (ctype))
-           {
-             decl = check_classfn (ctype, decl);
+      if (flags == NO_SPECIAL && declarator == cname)
+       DECL_CONSTRUCTOR_P (decl) = 1;
 
-             if (decl)
-               add_friend (current_class_type, decl);
-           }
-         else
-           cp_error ("member `%D' declared as friend before type `%T' defined",
-                     decl, ctype);
+      grokclassfn (ctype, decl, flags);
+
+      if (friend_depth)
+       {
+         if (!uses_template_parms_level (ctype, class_template_depth
+                                                + friend_depth))
+           template_member_p = true;
        }
-      else
+
+      /* A nested class may declare a member of an enclosing class
+        to be a friend, so we do lookup here even if CTYPE is in
+        the process of being defined.  */
+      if (class_template_depth
+         || COMPLETE_TYPE_P (ctype)
+         || (CLASS_TYPE_P (ctype) && TYPE_BEING_DEFINED (ctype)))
        {
-         /* Possibly a bunch of method friends.  */
+         if (DECL_TEMPLATE_INFO (decl))
+           /* DECL is a template specialization.  No need to
+              build a new TEMPLATE_DECL.  */
+           ;
+         else if (class_template_depth)
+           /* We rely on tsubst_friend_function to check the
+              validity of the declaration later.  */
+           decl = push_template_decl_real (decl, /*is_friend=*/true);
+         else
+           decl = check_classfn (ctype, decl,
+                                 template_member_p
+                                 ? current_template_parms
+                                 : NULL_TREE);
 
-         /* Get the class they belong to.  */
-         tree ctype = IDENTIFIER_TYPE_VALUE (cname);
-         tree fields = lookup_fnfields (TYPE_BINFO (ctype), declarator, 0);
+         if (template_member_p && decl && TREE_CODE (decl) == FUNCTION_DECL)
+           decl = DECL_TI_TEMPLATE (decl);
 
-         if (fields)
-           add_friends (current_class_type, declarator, ctype);
-         else
-           cp_error ("method `%D' is not a member of class `%T'",
-                     declarator, ctype);
-         decl = void_type_node;
+         if (decl)
+           add_friend (current_class_type, decl, /*complain=*/true);
        }
+      else
+       error ("member %qD declared as friend before type %qT defined",
+                 decl, ctype);
     }
   /* A global friend.
      @@ or possibly a friend from a base class ?!?  */
   else if (TREE_CODE (decl) == FUNCTION_DECL)
     {
+      int is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();
+
       /* Friends must all go through the overload machinery,
         even though they may not technically be overloaded.
 
         Note that because classes all wind up being top-level
         in their scope, their friend wind up in top-level scope as well.  */
-      DECL_ARGUMENTS (decl) = parmdecls;
       if (funcdef_flag)
-       DECL_CLASS_CONTEXT (decl) = current_class_type;
+       SET_DECL_FRIEND_CONTEXT (decl, current_class_type);
 
       if (! DECL_USE_TEMPLATE (decl))
        {
-         /* We can call pushdecl here, because the TREE_CHAIN of this
-            FUNCTION_DECL is not needed for other purposes.  Don't do
-            this for a template instantiation.  However, we don't
-            call pushdecl() for a friend function of a template
-            class, since in general, such a declaration depends on
-            template parameters.  Instead, we call pushdecl when the
-            class is instantiated.  */
-         if (!is_friend_template
-             && template_class_depth (current_class_type) == 0)
-           decl = pushdecl (decl);
-         else 
-           decl = push_template_decl_real (decl, /*is_friend=*/1); 
-
-         if (warn_nontemplate_friend
-             && ! funcdef_flag && ! flag_guiding_decls && ! is_friend_template
-             && current_template_parms && uses_template_parms (decl))
+         /* We must check whether the decl refers to template
+            arguments before push_template_decl_real adds a
+            reference to the containing template class.  */
+         int warn = (warn_nontemplate_friend
+                     && ! funcdef_flag && ! is_friend_template
+                     && current_template_parms
+                     && uses_template_parms (decl));
+
+         if (is_friend_template
+             || template_class_depth (current_class_type) != 0)
+           /* We can't call pushdecl for a template class, since in
+              general, such a declaration depends on template
+              parameters.  Instead, we call pushdecl when the class
+              is instantiated.  */
+           decl = push_template_decl_real (decl, /*is_friend=*/true);
+         else if (current_function_decl)
+           {
+             /* 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
+                class specialization, and pushdecl will insert an
+                unqualified friend decl into the template parameter
+                scope, rather than the namespace containing it.  */
+             tree ns = decl_namespace_context (decl);
+
+             push_nested_namespace (ns);
+             decl = pushdecl_namespace_level (decl, /*is_friend=*/true);
+             pop_nested_namespace (ns);
+           }
+
+         if (warn)
            {
              static int explained;
-             cp_warning ("friend declaration `%#D'", decl);
-             warning ("  declares a non-template function");
-             if (! explained)
+             bool warned;
+
+             warned = warning (OPT_Wnon_template_friend, "friend declaration "
+                               "%q#D declares a non-template function", decl);
+             if (! explained && warned)
                {
-                 warning ("  (if this is not what you intended, make sure");
-                 warning ("  the function template has already been declared,");
-                 warning ("  and add <> after the function name here)");
-                 warning ("  -Wno-non-template-friend disables this warning.");
+                 inform (input_location, "(if this is not what you intended, make sure "
+                         "the function template has already been declared "
+                         "and add <> after the function name here) ");
                  explained = 1;
                }
            }
        }
 
-      make_decl_rtl (decl, NULL_PTR, 1);
-      add_friend (current_class_type, 
-                 is_friend_template ? DECL_TI_TEMPLATE (decl) : decl);
+      if (decl == error_mark_node)
+       return error_mark_node;
+
+      add_friend (current_class_type,
+                 is_friend_template ? DECL_TI_TEMPLATE (decl) : decl,
+                 /*complain=*/true);
       DECL_FRIEND_P (decl) = 1;
     }
-  else
-    {
-      /* @@ Should be able to ingest later definitions of this function
-        before use.  */
-      tree decl = lookup_name_nonclass (declarator);
-      if (decl == NULL_TREE)
-       {
-         cp_warning ("implicitly declaring `%T' as struct", declarator);
-         decl = xref_tag (record_type_node, declarator, 1);
-         decl = TYPE_MAIN_DECL (decl);
-       }
 
-      /* Allow abbreviated declarations of overloaded functions,
-        but not if those functions are really class names.  */
-      if (TREE_CODE (decl) == TREE_LIST && TREE_TYPE (TREE_PURPOSE (decl)))
-       {
-         cp_warning ("`friend %T' archaic, use `friend class %T' instead",
-                     declarator, declarator);
-         decl = TREE_TYPE (TREE_PURPOSE (decl));
-       }
-
-      if (TREE_CODE (decl) == TREE_LIST)
-       add_friends (current_class_type, TREE_PURPOSE (decl), NULL_TREE);
-      else
-       make_friend_class (current_class_type, TREE_TYPE (decl));
-      decl = void_type_node;
-    }
   return decl;
 }