]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/cp/method.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / cp / method.c
index 5bb555d2e04ed4442a45cad7f4cff7dbf9b42dde..7c63d899b735dfc888505d4094dfcbdceef43a74 100644 (file)
@@ -1,40 +1,44 @@
 /* Handle the hair of processing (but not expanding) inline functions.
    Also manage function and variable name overloading.
    Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008
+   Free Software Foundation, Inc.
    Contributed by Michael Tiemann (tiemann@cygnus.com)
 
-This file is part of GNU CC.
-   
-GNU CC is free software; you can redistribute it and/or modify
+This file is part of GCC.
+
+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/>.  */
 
 
 /* Handle method declarations.  */
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "tree.h"
 #include "cp-tree.h"
-#include "obstack.h"
 #include "rtl.h"
 #include "expr.h"
 #include "output.h"
 #include "flags.h"
 #include "toplev.h"
-#include "ggc.h"
 #include "tm_p.h"
+#include "target.h"
+#include "tree-pass.h"
+#include "diagnostic.h"
+#include "cgraph.h"
 
 /* Various flags to control the mangling process.  */
 
@@ -49,332 +53,322 @@ enum mangling_flags
   mf_maybe_uninstantiated = 1,
   /* When mangling a numeric value, use the form `_XX_' (instead of
      just `XX') if the value has more than one digit.  */
-  mf_use_underscores_around_value = 2,
+  mf_use_underscores_around_value = 2
 };
 
 typedef enum mangling_flags mangling_flags;
 
-#define obstack_chunk_alloc xmalloc
-#define obstack_chunk_free free
-
-static void do_build_assign_ref PARAMS ((tree));
-static void do_build_copy_constructor PARAMS ((tree));
-static tree synthesize_exception_spec PARAMS ((tree, tree (*) (tree, void *), void *));
-static tree locate_dtor PARAMS ((tree, void *));
-static tree locate_ctor PARAMS ((tree, void *));
-static tree locate_copy PARAMS ((tree, void *));
+static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
+static void do_build_assign_ref (tree);
+static void do_build_copy_constructor (tree);
+static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *);
+static tree make_alias_for_thunk (tree);
 
 /* Called once to initialize method.c.  */
 
 void
-init_method ()
+init_method (void)
 {
   init_mangle ();
 }
-
 \f
-/* Set the mangled name (DECL_ASSEMBLER_NAME) for DECL.  */
+/* Return a this or result adjusting thunk to FUNCTION.  THIS_ADJUSTING
+   indicates whether it is a this or result adjusting thunk.
+   FIXED_OFFSET and VIRTUAL_OFFSET indicate how to do the adjustment
+   (see thunk_adjust).  VIRTUAL_OFFSET can be NULL, but FIXED_OFFSET
+   never is.  VIRTUAL_OFFSET is the /index/ into the vtable for this
+   adjusting thunks, we scale it to a byte offset. For covariant
+   thunks VIRTUAL_OFFSET is the virtual binfo.  You must post process
+   the returned thunk with finish_thunk.  */
 
-void
-set_mangled_name_for_decl (decl)
-     tree decl;
+tree
+make_thunk (tree function, bool this_adjusting,
+           tree fixed_offset, tree virtual_offset)
 {
-  if (processing_template_decl)
-    /* There's no need to mangle the name of a template function.  */
-    return;
-
-  mangle_decl (decl);
-}
-
-\f
-/* Given a tree_code CODE, and some arguments (at least one),
-   attempt to use an overloaded operator on the arguments.
-
-   For unary operators, only the first argument need be checked.
-   For binary operators, both arguments may need to be checked.
+  HOST_WIDE_INT d;
+  tree thunk;
 
-   Member functions can convert class references to class pointers,
-   for one-level deep indirection.  More than that is not supported.
-   Operators [](), ()(), and ->() must be member functions.
+  gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
+  /* We can have this thunks to covariant thunks, but not vice versa.  */
+  gcc_assert (!DECL_THIS_THUNK_P (function));
+  gcc_assert (!DECL_RESULT_THUNK_P (function) || this_adjusting);
 
-   We call function call building calls with LOOKUP_COMPLAIN if they
-   are our only hope.  This is true when we see a vanilla operator
-   applied to something of aggregate type.  If this fails, we are free
-   to return `error_mark_node', because we will have reported the
-   error.
+  /* Scale the VIRTUAL_OFFSET to be in terms of bytes.  */
+  if (this_adjusting && virtual_offset)
+    virtual_offset
+      = size_binop (MULT_EXPR,
+                   virtual_offset,
+                   convert (ssizetype,
+                            TYPE_SIZE_UNIT (vtable_entry_type)));
 
-   Operators NEW and DELETE overload in funny ways: operator new takes
-   a single `size' parameter, and operator delete takes a pointer to the
-   storage being deleted.  When overloading these operators, success is
-   assumed.  If there is a failure, report an error message and return
-   `error_mark_node'.  */
+  d = tree_low_cst (fixed_offset, 0);
+
+  /* See if we already have the thunk in question.  For this_adjusting
+     thunks VIRTUAL_OFFSET will be an INTEGER_CST, for covariant thunks it
+     will be a BINFO.  */
+  for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
+    if (DECL_THIS_THUNK_P (thunk) == this_adjusting
+       && THUNK_FIXED_OFFSET (thunk) == d
+       && !virtual_offset == !THUNK_VIRTUAL_OFFSET (thunk)
+       && (!virtual_offset
+           || (this_adjusting
+               ? tree_int_cst_equal (THUNK_VIRTUAL_OFFSET (thunk),
+                                     virtual_offset)
+               : THUNK_VIRTUAL_OFFSET (thunk) == virtual_offset)))
+      return thunk;
+
+  /* All thunks must be created before FUNCTION is actually emitted;
+     the ABI requires that all thunks be emitted together with the
+     function to which they transfer control.  */
+  gcc_assert (!TREE_ASM_WRITTEN (function));
+  /* Likewise, we can only be adding thunks to a function declared in
+     the class currently being laid out.  */
+  gcc_assert (TYPE_SIZE (DECL_CONTEXT (function))
+             && TYPE_BEING_DEFINED (DECL_CONTEXT (function)));
+
+  thunk = build_decl (FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
+  DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
+  cxx_dup_lang_specific_decl (thunk);
+  DECL_THUNKS (thunk) = NULL_TREE;
+
+  DECL_CONTEXT (thunk) = DECL_CONTEXT (function);
+  TREE_READONLY (thunk) = TREE_READONLY (function);
+  TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
+  TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
+  SET_DECL_THUNK_P (thunk, this_adjusting);
+  THUNK_TARGET (thunk) = function;
+  THUNK_FIXED_OFFSET (thunk) = d;
+  THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
+  THUNK_ALIAS (thunk) = NULL_TREE;
+
+  /* The thunk itself is not a constructor or destructor, even if
+     the thing it is thunking to is.  */
+  DECL_INTERFACE_KNOWN (thunk) = 1;
+  DECL_NOT_REALLY_EXTERN (thunk) = 1;
+  DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
+  DECL_DESTRUCTOR_P (thunk) = 0;
+  DECL_CONSTRUCTOR_P (thunk) = 0;
+  DECL_EXTERNAL (thunk) = 1;
+  DECL_ARTIFICIAL (thunk) = 1;
+  /* Even if this thunk is a member of a local class, we don't
+     need a static chain.  */
+  DECL_NO_STATIC_CHAIN (thunk) = 1;
+  /* The THUNK is not a pending inline, even if the FUNCTION is.  */
+  DECL_PENDING_INLINE_P (thunk) = 0;
+  DECL_DECLARED_INLINE_P (thunk) = 0;
+  /* Nor has it been deferred.  */
+  DECL_DEFERRED_FN (thunk) = 0;
+  /* Nor is it a template instantiation.  */
+  DECL_USE_TEMPLATE (thunk) = 0;
+  DECL_TEMPLATE_INFO (thunk) = NULL;
+
+  /* Add it to the list of thunks associated with FUNCTION.  */
+  TREE_CHAIN (thunk) = DECL_THUNKS (function);
+  DECL_THUNKS (function) = thunk;
 
-/* NOSTRICT */
-tree
-build_opfncall (code, flags, xarg1, xarg2, arg3)
-     enum tree_code code;
-     int flags;
-     tree xarg1, xarg2, arg3;
-{
-  return build_new_op (code, flags, xarg1, xarg2, arg3);
+  return thunk;
 }
-\f
-/* This function takes an identifier, ID, and attempts to figure out what
-   it means. There are a number of possible scenarios, presented in increasing
-   order of hair:
-
-   1) not in a class's scope
-   2) in class's scope, member name of the class's method
-   3) in class's scope, but not a member name of the class
-   4) in class's scope, member name of a class's variable
-
-   NAME is $1 from the bison rule. It is an IDENTIFIER_NODE.
-   VALUE is $$ from the bison rule. It is the value returned by lookup_name ($1)
-
-   As a last ditch, try to look up the name as a label and return that
-   address.
 
-   Values which are declared as being of REFERENCE_TYPE are
-   automatically dereferenced here (as a hack to make the
-   compiler faster).  */
+/* Finish THUNK, a thunk decl.  */
 
-tree
-hack_identifier (value, name)
-     tree value, name;
+void
+finish_thunk (tree thunk)
 {
-  tree type;
-
-  if (value == error_mark_node)
-    return error_mark_node;
-
-  type = TREE_TYPE (value);
-  if (TREE_CODE (value) == FIELD_DECL)
+  tree function, name;
+  tree fixed_offset = ssize_int (THUNK_FIXED_OFFSET (thunk));
+  tree virtual_offset = THUNK_VIRTUAL_OFFSET (thunk);
+
+  gcc_assert (!DECL_NAME (thunk) && DECL_THUNK_P (thunk));
+  if (virtual_offset && DECL_RESULT_THUNK_P (thunk))
+    virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
+  function = THUNK_TARGET (thunk);
+  name = mangle_thunk (function, DECL_THIS_THUNK_P (thunk),
+                      fixed_offset, virtual_offset);
+
+  /* We can end up with declarations of (logically) different
+     covariant thunks, that do identical adjustments.  The two thunks
+     will be adjusting between within different hierarchies, which
+     happen to have the same layout.  We must nullify one of them to
+     refer to the other.  */
+  if (DECL_RESULT_THUNK_P (thunk))
     {
-      if (current_class_ptr == NULL_TREE)
-       {
-         if (current_function_decl 
-             && DECL_STATIC_FUNCTION_P (current_function_decl))
-           error ("invalid use of member `%D' in static member function",
-                     value);
-         else
-           /* We can get here when processing a bad default
-              argument, like:
-                struct S { int a; void f(int i = a); }  */
-           error ("invalid use of member `%D'", value);
-
-         return error_mark_node;
-       }
-      TREE_USED (current_class_ptr) = 1;
-
-      /* Mark so that if we are in a constructor, and then find that
-        this field was initialized by a base initializer,
-        we can emit an error message.  */
-      TREE_USED (value) = 1;
-      value = build_component_ref (current_class_ref, name, NULL_TREE, 1);
+      tree cov_probe;
+
+      for (cov_probe = DECL_THUNKS (function);
+          cov_probe; cov_probe = TREE_CHAIN (cov_probe))
+       if (DECL_NAME (cov_probe) == name)
+         {
+           gcc_assert (!DECL_THUNKS (thunk));
+           THUNK_ALIAS (thunk) = (THUNK_ALIAS (cov_probe)
+                                  ? THUNK_ALIAS (cov_probe) : cov_probe);
+           break;
+         }
     }
-  else if ((TREE_CODE (value) == FUNCTION_DECL
-           && DECL_FUNCTION_MEMBER_P (value))
-          || (TREE_CODE (value) == OVERLOAD
-              && DECL_FUNCTION_MEMBER_P (OVL_CURRENT (value))))
-    {
-      tree decl;
 
-      if (TREE_CODE (value) == OVERLOAD)
-       value = OVL_CURRENT (value);
+  DECL_NAME (thunk) = name;
+  SET_DECL_ASSEMBLER_NAME (thunk, name);
+}
 
-      decl = maybe_dummy_object (DECL_CONTEXT (value), 0);
-      value = build_component_ref (decl, name, NULL_TREE, 1);
-    }
-  else if (really_overloaded_fn (value))
-    ;
-  else if (TREE_CODE (value) == OVERLOAD)
-    /* not really overloaded function */
-    mark_used (OVL_FUNCTION (value));
-  else if (TREE_CODE (value) == TREE_LIST)
-    {
-      /* Ambiguous reference to base members, possibly other cases?.  */
-      tree t = value;
-      while (t && TREE_CODE (t) == TREE_LIST)
-       {
-         mark_used (TREE_VALUE (t));
-         t = TREE_CHAIN (t);
-       }
-    }
-  else if (TREE_CODE (value) == NAMESPACE_DECL)
-    {
-      error ("use of namespace `%D' as expression", value);
-      return error_mark_node;
-    }
-  else if (DECL_CLASS_TEMPLATE_P (value))
-    {
-      error ("use of class template `%T' as expression", value);
-      return error_mark_node;
-    }
-  else
-    mark_used (value);
+/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
+   offset indicated by VIRTUAL_OFFSET, if that is
+   non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
+   zero for a result adjusting thunk.  */
 
-  if (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == PARM_DECL
-      || TREE_CODE (value) == RESULT_DECL)
+static tree
+thunk_adjust (tree ptr, bool this_adjusting,
+             HOST_WIDE_INT fixed_offset, tree virtual_offset)
+{
+  if (this_adjusting)
+    /* Adjust the pointer by the constant.  */
+    ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
+                      size_int (fixed_offset));
+
+  /* If there's a virtual offset, look up that value in the vtable and
+     adjust the pointer again.  */
+  if (virtual_offset)
     {
-      tree context = decl_function_context (value);
-      if (context != NULL_TREE && context != current_function_decl
-         && ! TREE_STATIC (value))
-       {
-         error ("use of %s from containing function",
-                     (TREE_CODE (value) == VAR_DECL
-                      ? "`auto' variable" : "parameter"));
-         cp_error_at ("  `%#D' declared here", value);
-         value = error_mark_node;
-       }
+      tree vtable;
+
+      ptr = save_expr (ptr);
+      /* The vptr is always at offset zero in the object.  */
+      vtable = build1 (NOP_EXPR,
+                      build_pointer_type (build_pointer_type
+                                          (vtable_entry_type)),
+                      ptr);
+      /* Form the vtable address.  */
+      vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
+      /* Find the entry with the vcall offset.  */
+      vtable = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (vtable), vtable,
+                      fold_convert (sizetype, virtual_offset));
+      /* Get the offset itself.  */
+      vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
+      /* Adjust the `this' pointer.  */
+      ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
+                        fold_convert (sizetype, vtable));
     }
 
-  if (DECL_P (value) && DECL_NONLOCAL (value))
-    {
-      if (DECL_CLASS_SCOPE_P (value)
-         && DECL_CONTEXT (value) != current_class_type)
-       {
-         tree path;
-         path = currently_open_derived_class (DECL_CONTEXT (value));
-         enforce_access (path, value);
-       }
-    }
-  else if (TREE_CODE (value) == TREE_LIST 
-          && TREE_TYPE (value) == error_mark_node)
-    {
-      error ("\
-request for member `%D' is ambiguous in multiple inheritance lattice",
-               name);
-      print_candidates (value);
-      return error_mark_node;
-    }
+  if (!this_adjusting)
+    /* Adjust the pointer by the constant.  */
+    ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
+                      size_int (fixed_offset));
 
-  if (! processing_template_decl)
-    value = convert_from_reference (value);
-  return value;
+  return ptr;
 }
 
-\f
-/* Return a thunk to FUNCTION.  For a virtual thunk, DELTA is the
-   offset to this used to locate the vptr, and VCALL_INDEX is used to
-   look up the eventual subobject location.  For a non-virtual thunk,
-   DELTA is the offset to this and VCALL_INDEX is NULL.  */
+static GTY (()) int thunk_labelno;
+
+/* Create a static alias to function.  */
 
 tree
-make_thunk (function, delta, vcall_index)
-     tree function;
-     tree delta;
-     tree vcall_index;
+make_alias_for (tree function, tree newid)
 {
-  tree thunk_id;
-  tree thunk;
-  tree func_decl;
-  tree vcall_offset;
-  HOST_WIDE_INT d;
+  tree alias = build_decl (FUNCTION_DECL, newid, TREE_TYPE (function));
+  DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
+  cxx_dup_lang_specific_decl (alias);
+  DECL_CONTEXT (alias) = NULL;
+  TREE_READONLY (alias) = TREE_READONLY (function);
+  TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
+  TREE_PUBLIC (alias) = 0;
+  DECL_INTERFACE_KNOWN (alias) = 1;
+  DECL_NOT_REALLY_EXTERN (alias) = 1;
+  DECL_THIS_STATIC (alias) = 1;
+  DECL_SAVED_FUNCTION_DATA (alias) = NULL;
+  DECL_DESTRUCTOR_P (alias) = 0;
+  DECL_CONSTRUCTOR_P (alias) = 0;
+  DECL_CLONED_FUNCTION (alias) = NULL_TREE;
+  DECL_EXTERNAL (alias) = 0;
+  DECL_ARTIFICIAL (alias) = 1;
+  DECL_NO_STATIC_CHAIN (alias) = 1;
+  DECL_PENDING_INLINE_P (alias) = 0;
+  DECL_DECLARED_INLINE_P (alias) = 0;
+  DECL_DEFERRED_FN (alias) = 0;
+  DECL_USE_TEMPLATE (alias) = 0;
+  DECL_TEMPLATE_INSTANTIATED (alias) = 0;
+  DECL_TEMPLATE_INFO (alias) = NULL;
+  DECL_INITIAL (alias) = error_mark_node;
+  TREE_ADDRESSABLE (alias) = 1;
+  TREE_USED (alias) = 1;
+  SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
+  TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
+  return alias;
+}
 
-  /* Scale the VCALL_INDEX to be in terms of bytes.  */
-  if (vcall_index)
-    vcall_offset 
-      = size_binop (MULT_EXPR,
-                   vcall_index,
-                   convert (ssizetype,
-                            TYPE_SIZE_UNIT (vtable_entry_type)));
-  else
-    vcall_offset = NULL_TREE;
+static tree
+make_alias_for_thunk (tree function)
+{
+  tree alias;
+  char buf[256];
 
-  d = tree_low_cst (delta, 0);
+  ASM_GENERATE_INTERNAL_LABEL (buf, "LTHUNK", thunk_labelno);
+  thunk_labelno++;
 
-  if (TREE_CODE (function) != ADDR_EXPR)
-    abort ();
-  func_decl = TREE_OPERAND (function, 0);
-  if (TREE_CODE (func_decl) != FUNCTION_DECL)
-    abort ();
+  alias = make_alias_for (function, get_identifier (buf));
 
-  thunk_id = mangle_thunk (TREE_OPERAND (function, 0), 
-                          delta, vcall_offset);
-  thunk = IDENTIFIER_GLOBAL_VALUE (thunk_id);
-  if (thunk && !DECL_THUNK_P (thunk))
-    {
-      error ("implementation-reserved name `%D' used", thunk_id);
-      thunk = NULL_TREE;
-      SET_IDENTIFIER_GLOBAL_VALUE (thunk_id, thunk);
-    }
-  if (thunk == NULL_TREE)
-    {
-      thunk = build_decl (FUNCTION_DECL, thunk_id, TREE_TYPE (func_decl));
-      DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (func_decl);
-      copy_lang_decl (func_decl);
-      SET_DECL_ASSEMBLER_NAME (thunk, thunk_id);
-      DECL_CONTEXT (thunk) = DECL_CONTEXT (func_decl);
-      TREE_READONLY (thunk) = TREE_READONLY (func_decl);
-      TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (func_decl);
-      TREE_PUBLIC (thunk) = TREE_PUBLIC (func_decl);
-      if (flag_weak)
-       comdat_linkage (thunk);
-      SET_DECL_THUNK_P (thunk);
-      DECL_INITIAL (thunk) = function;
-      THUNK_DELTA (thunk) = d;
-      THUNK_VCALL_OFFSET (thunk) = vcall_offset;
-      /* The thunk itself is not a constructor or destructor, even if
-         the thing it is thunking to is.  */
-      DECL_INTERFACE_KNOWN (thunk) = 1;
-      DECL_NOT_REALLY_EXTERN (thunk) = 1;
-      DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
-      DECL_DESTRUCTOR_P (thunk) = 0;
-      DECL_CONSTRUCTOR_P (thunk) = 0;
-      /* And neither is it a clone.  */
-      DECL_CLONED_FUNCTION (thunk) = NULL_TREE;
-      DECL_EXTERNAL (thunk) = 1;
-      DECL_ARTIFICIAL (thunk) = 1;
-      /* Even if this thunk is a member of a local class, we don't
-        need a static chain.  */
-      DECL_NO_STATIC_CHAIN (thunk) = 1;
-      /* The THUNK is not a pending inline, even if the FUNC_DECL is.  */
-      DECL_PENDING_INLINE_P (thunk) = 0;
-      /* Nor has it been deferred.  */
-      DECL_DEFERRED_FN (thunk) = 0;
-      /* So that finish_file can write out any thunks that need to be: */
-      pushdecl_top_level (thunk);
-      SET_IDENTIFIER_GLOBAL_VALUE (thunk_id, thunk);
-    }
-  return thunk;
+  if (!flag_syntax_only)
+    assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
+
+  return alias;
 }
 
-/* Emit the definition of a C++ multiple inheritance vtable thunk.  If
-   EMIT_P is non-zero, the thunk is emitted immediately.  */
+/* Emit the definition of a C++ multiple inheritance or covariant
+   return vtable thunk.  If EMIT_P is nonzero, the thunk is emitted
+   immediately.  */
 
 void
-use_thunk (thunk_fndecl, emit_p)
-     tree thunk_fndecl;
-     int emit_p;
+use_thunk (tree thunk_fndecl, bool emit_p)
 {
-  tree fnaddr;
-  tree function;
-  tree vcall_offset;
-  HOST_WIDE_INT delta;
+  tree a, t, function, alias;
+  tree virtual_offset;
+  HOST_WIDE_INT fixed_offset, virtual_value;
+  bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);
+
+  /* We should have called finish_thunk to give it a name.  */
+  gcc_assert (DECL_NAME (thunk_fndecl));
+
+  /* We should never be using an alias, always refer to the
+     aliased thunk.  */
+  gcc_assert (!THUNK_ALIAS (thunk_fndecl));
 
   if (TREE_ASM_WRITTEN (thunk_fndecl))
     return;
-  
-  fnaddr = DECL_INITIAL (thunk_fndecl);
-  if (TREE_CODE (DECL_INITIAL (thunk_fndecl)) != ADDR_EXPR)
+
+  function = THUNK_TARGET (thunk_fndecl);
+  if (DECL_RESULT (thunk_fndecl))
     /* We already turned this thunk into an ordinary function.
        There's no need to process this thunk again.  */
     return;
 
+  if (DECL_THUNK_P (function))
+    /* The target is itself a thunk, process it now.  */
+    use_thunk (function, emit_p);
+
   /* Thunks are always addressable; they only appear in vtables.  */
   TREE_ADDRESSABLE (thunk_fndecl) = 1;
 
   /* Figure out what function is being thunked to.  It's referenced in
      this translation unit.  */
-  function = TREE_OPERAND (fnaddr, 0);
   TREE_ADDRESSABLE (function) = 1;
   mark_used (function);
-  TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (function)) = 1;
   if (!emit_p)
     return;
 
-  delta = THUNK_DELTA (thunk_fndecl);
-  vcall_offset = THUNK_VCALL_OFFSET (thunk_fndecl);
+  if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function))
+   alias = make_alias_for_thunk (function);
+  else
+   alias = function;
+
+  fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
+  virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);
+
+  if (virtual_offset)
+    {
+      if (!this_adjusting)
+       virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
+      virtual_value = tree_low_cst (virtual_offset, /*pos=*/0);
+      gcc_assert (virtual_value);
+    }
+  else
+    virtual_value = 0;
 
   /* And, if we need to emit the thunk, it's used.  */
   mark_used (thunk_fndecl);
@@ -383,6 +377,11 @@ use_thunk (thunk_fndecl, emit_p)
   /* The linkage of the function may have changed.  FIXME in linkage
      rewrite.  */
   TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function);
+  DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function);
+  DECL_VISIBILITY_SPECIFIED (thunk_fndecl)
+    = DECL_VISIBILITY_SPECIFIED (function);
+  if (DECL_ONE_ONLY (function) || DECL_WEAK (function))
+    make_decl_one_only (thunk_fndecl);
 
   if (flag_syntax_only)
     {
@@ -392,107 +391,138 @@ use_thunk (thunk_fndecl, emit_p)
 
   push_to_top_level ();
 
-  /* The back-end expects DECL_INITIAL to contain a BLOCK, so we
-     create one.  */
-  DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
-  BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) 
-    = DECL_ARGUMENTS (thunk_fndecl);
+  if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
+      && targetm.have_named_sections)
+    {
+      resolve_unique_section (function, 0, flag_function_sections);
 
-#ifdef ASM_OUTPUT_MI_THUNK
-  if (!vcall_offset)
+      if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function))
+       {
+         resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+
+         /* Output the thunk into the same section as function.  */
+         DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function);
+       }
+    }
+
+  /* Set up cloned argument trees for the thunk.  */
+  t = NULL_TREE;
+  for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
+    {
+      tree x = copy_node (a);
+      TREE_CHAIN (x) = t;
+      DECL_CONTEXT (x) = thunk_fndecl;
+      SET_DECL_RTL (x, NULL_RTX);
+      DECL_HAS_VALUE_EXPR_P (x) = 0;
+      t = x;
+    }
+  a = nreverse (t);
+  DECL_ARGUMENTS (thunk_fndecl) = a;
+
+  if (this_adjusting
+      && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+                                             virtual_value, alias))
     {
       const char *fnname;
+      tree fn_block;
+      
       current_function_decl = thunk_fndecl;
       DECL_RESULT (thunk_fndecl)
        = build_decl (RESULT_DECL, 0, integer_type_node);
-      fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
-      init_function_start (thunk_fndecl, input_filename, lineno);
-      current_function_is_thunk = 1;
+      fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
+      /* The back end expects DECL_INITIAL to contain a BLOCK, so we
+        create one.  */
+      fn_block = make_node (BLOCK);
+      BLOCK_VARS (fn_block) = a;
+      DECL_INITIAL (thunk_fndecl) = fn_block;
+      init_function_start (thunk_fndecl);
+      cfun->is_thunk = 1;
       assemble_start_function (thunk_fndecl, fnname);
-      ASM_OUTPUT_MI_THUNK (asm_out_file, thunk_fndecl, delta, function);
+
+      targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
+                                      fixed_offset, virtual_value, alias);
+
       assemble_end_function (thunk_fndecl, fnname);
+      init_insn_lengths ();
       current_function_decl = 0;
-      cfun = 0;
+      set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
     }
   else
-#endif /* ASM_OUTPUT_MI_THUNK */
     {
-      /* If we don't have the necessary macro for efficient thunks, generate
-        a thunk function that just makes a call to the real function.
-        Unfortunately, this doesn't work for varargs.  */
-
-      tree a, t;
+      int i;
+      tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
+      /* If this is a covariant thunk, or we don't have the necessary
+        code for efficient thunks, generate a thunk function that
+        just makes a call to the real function.  Unfortunately, this
+        doesn't work for varargs.  */
 
       if (varargs_function_p (function))
-       error ("generic thunk code fails for method `%#D' which uses `...'",
+       error ("generic thunk code fails for method %q#D which uses %<...%>",
               function);
 
-      /* Set up clone argument trees for the thunk.  */
-      t = NULL_TREE;
-      for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
-       {
-         tree x = copy_node (a);
-         TREE_CHAIN (x) = t;
-         DECL_CONTEXT (x) = thunk_fndecl;
-         t = x;
-       }
-      a = nreverse (t);
-      DECL_ARGUMENTS (thunk_fndecl) = a;
       DECL_RESULT (thunk_fndecl) = NULL_TREE;
 
-      start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
+      start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
       /* We don't bother with a body block for thunks.  */
 
-      /* Adjust the this pointer by the constant.  */
-      t = ssize_int (delta);
-      t = fold (build (PLUS_EXPR, TREE_TYPE (a), a, t));
+      /* There's no need to check accessibility inside the thunk body.  */
+      push_deferring_access_checks (dk_no_check);
 
-      /* If there's a vcall offset, look up that value in the vtable and
-        adjust the `this' pointer again.  */
-      if (vcall_offset && !integer_zerop (vcall_offset))
-       {
-         tree orig_this;
-
-         t = save_expr (t);
-         orig_this = t;
-         /* The vptr is always at offset zero in the object.  */
-         t = build1 (NOP_EXPR,
-                     build_pointer_type (build_pointer_type 
-                                         (vtable_entry_type)),
-                     t);
-         /* Form the vtable address.  */
-         t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
-         /* Find the entry with the vcall offset.  */
-         t = build (PLUS_EXPR, TREE_TYPE (t), t, vcall_offset);
-         /* Calculate the offset itself.  */
-         t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
-         /* Adjust the `this' pointer.  */
-         t = fold (build (PLUS_EXPR,
-                          TREE_TYPE (orig_this),
-                          orig_this,
-                          t));
-       }
+      t = a;
+      if (this_adjusting)
+       t = thunk_adjust (t, /*this_adjusting=*/1,
+                         fixed_offset, virtual_offset);
 
       /* Build up the call to the real function.  */
-      t = tree_cons (NULL_TREE, t, NULL_TREE);
-      for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
-       t = tree_cons (NULL_TREE, a, t);
-      t = nreverse (t);
-      t = build_call (function, t);
+      argarray[0] = t;
+      for (i = 1, a = TREE_CHAIN (a); a; a = TREE_CHAIN (a), i++)
+       argarray[i] = a;
+      t = build_call_a (alias, i, argarray);
+      CALL_FROM_THUNK_P (t) = 1;
+      CALL_CANNOT_INLINE_P (t) = 1;
+
       if (VOID_TYPE_P (TREE_TYPE (t)))
        finish_expr_stmt (t);
       else
-       finish_return_stmt (t);
+       {
+         if (!this_adjusting)
+           {
+             tree cond = NULL_TREE;
+
+             if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
+               {
+                 /* If the return type is a pointer, we need to
+                    protect against NULL.  We know there will be an
+                    adjustment, because that's why we're emitting a
+                    thunk.  */
+                 t = save_expr (t);
+                 cond = cp_convert (boolean_type_node, t);
+               }
+
+             t = thunk_adjust (t, /*this_adjusting=*/0,
+                               fixed_offset, virtual_offset);
+             if (cond)
+               t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
+                           cp_convert (TREE_TYPE (t), integer_zero_node));
+           }
+         if (MAYBE_CLASS_TYPE_P (TREE_TYPE (t)))
+           t = build_cplus_new (TREE_TYPE (t), t);
+         finish_return_stmt (t);
+       }
 
       /* Since we want to emit the thunk, we explicitly mark its name as
         referenced.  */
-      TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (thunk_fndecl)) = 1;
+      mark_decl_referenced (thunk_fndecl);
 
       /* But we don't want debugging information about it.  */
       DECL_IGNORED_P (thunk_fndecl) = 1;
 
-      expand_body (finish_function (0));
+      /* Re-enable access control.  */
+      pop_deferring_access_checks ();
+
+      thunk_fndecl = finish_function (0);
+      cgraph_add_new_function (thunk_fndecl, false);
     }
 
   pop_from_top_level ();
@@ -503,11 +533,9 @@ use_thunk (thunk_fndecl, emit_p)
 /* Generate code for default X(X&) constructor.  */
 
 static void
-do_build_copy_constructor (fndecl)
-     tree fndecl;
+do_build_copy_constructor (tree fndecl)
 {
   tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
-  tree t;
 
   parm = convert_from_reference (parm);
 
@@ -517,69 +545,64 @@ do_build_copy_constructor (fndecl)
        if *this is a base subobject.  */;
   else if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type))
     {
-      t = build (INIT_EXPR, void_type_node, current_class_ref, parm);
+      tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm);
       finish_expr_stmt (t);
     }
   else
     {
       tree fields = TYPE_FIELDS (current_class_type);
-      int n_bases = CLASSTYPE_N_BASECLASSES (current_class_type);
-      tree binfos = TYPE_BINFO_BASETYPES (current_class_type);
       tree member_init_list = NULL_TREE;
-      tree base_init_list = NULL_TREE;
       int cvquals = cp_type_quals (TREE_TYPE (parm));
       int i;
+      tree binfo, base_binfo;
+      VEC(tree,gc) *vbases;
 
       /* Initialize all the base-classes with the parameter converted
         to their type so that we get their copy constructor and not
         another constructor that takes current_class_type.  We must
         deal with the binfo's directly as a direct base might be
         inaccessible due to ambiguity.  */
-      for (t = CLASSTYPE_VBASECLASSES (current_class_type); t;
-          t = TREE_CHAIN (t))
+      for (vbases = CLASSTYPE_VBASECLASSES (current_class_type), i = 0;
+          VEC_iterate (tree, vbases, i, binfo); i++)
        {
-         tree binfo = TREE_VALUE (t);
-         
-         base_init_list = tree_cons (binfo,
-                                     build_base_path (PLUS_EXPR, parm,
-                                                      binfo, 1),
-                                     base_init_list);
+         member_init_list
+           = tree_cons (binfo,
+                        build_tree_list (NULL_TREE,
+                                         build_base_path (PLUS_EXPR, parm,
+                                                          binfo, 1)),
+                        member_init_list);
        }
 
-      for (i = 0; i < n_bases; ++i)
+      for (binfo = TYPE_BINFO (current_class_type), i = 0;
+          BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
        {
-         tree binfo = TREE_VEC_ELT (binfos, i);
-         if (TREE_VIA_VIRTUAL (binfo))
-           continue; 
-
-         base_init_list = tree_cons (binfo,
-                                     build_base_path (PLUS_EXPR, parm,
-                                                      binfo, 1),
-                                     base_init_list);
+         if (BINFO_VIRTUAL_P (base_binfo))
+           continue;
+
+         member_init_list
+           = tree_cons (base_binfo,
+                        build_tree_list (NULL_TREE,
+                                         build_base_path (PLUS_EXPR, parm,
+                                                          base_binfo, 1)),
+                        member_init_list);
        }
 
       for (; fields; fields = TREE_CHAIN (fields))
        {
-         tree init;
+         tree init = parm;
          tree field = fields;
          tree expr_type;
 
          if (TREE_CODE (field) != FIELD_DECL)
            continue;
 
-         init = parm;
+         expr_type = TREE_TYPE (field);
          if (DECL_NAME (field))
            {
              if (VFIELD_NAME_P (DECL_NAME (field)))
                continue;
-
-             /* True for duplicate members.  */
-             if (IDENTIFIER_CLASS_VALUE (DECL_NAME (field)) != field)
-               continue;
            }
-         else if ((t = TREE_TYPE (field)) != NULL_TREE
-                  && ANON_AGGR_TYPE_P (t)
-                  && TYPE_FIELDS (t) != NULL_TREE)
+         else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type))
            /* Just use the field; anonymous types can't have
               nontrivial copy ctors or assignment ops.  */;
          else
@@ -590,29 +613,31 @@ do_build_copy_constructor (fndecl)
             the field is "T", then the type will usually be "const
             T".  (There are no cv-qualified variants of reference
             types.)  */
-         expr_type = TREE_TYPE (field);
          if (TREE_CODE (expr_type) != REFERENCE_TYPE)
-           expr_type = cp_build_qualified_type (expr_type, cvquals);
-         init = build (COMPONENT_REF, expr_type, init, field);
+           {
+             int quals = cvquals;
+
+             if (DECL_MUTABLE_P (field))
+               quals &= ~TYPE_QUAL_CONST;
+             expr_type = cp_build_qualified_type (expr_type, quals);
+           }
+
+         init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
          init = build_tree_list (NULL_TREE, init);
 
-         member_init_list
-           = tree_cons (field, init, member_init_list);
+         member_init_list = tree_cons (field, init, member_init_list);
        }
-      member_init_list = nreverse (member_init_list);
-      base_init_list = nreverse (base_init_list);
-      emit_base_init (member_init_list, base_init_list);
+      finish_mem_initializers (member_init_list);
     }
 }
 
 static void
-do_build_assign_ref (fndecl)
-     tree fndecl;
+do_build_assign_ref (tree fndecl)
 {
   tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
   tree compound_stmt;
 
-  compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
+  compound_stmt = begin_compound_stmt (0);
   parm = convert_from_reference (parm);
 
   if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)
@@ -621,138 +646,148 @@ do_build_assign_ref (fndecl)
        if *this is a base subobject.  */;
   else if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type))
     {
-      tree t = build (MODIFY_EXPR, void_type_node, current_class_ref, parm);
+      tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm);
       finish_expr_stmt (t);
     }
   else
     {
-      tree fields = TYPE_FIELDS (current_class_type);
-      int n_bases = CLASSTYPE_N_BASECLASSES (current_class_type);
-      tree binfos = TYPE_BINFO_BASETYPES (current_class_type);
+      tree fields;
       int cvquals = cp_type_quals (TREE_TYPE (parm));
       int i;
+      tree binfo, base_binfo;
 
-      for (i = 0; i < n_bases; ++i)
+      /* Assign to each of the direct base classes.  */
+      for (binfo = TYPE_BINFO (current_class_type), i = 0;
+          BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
        {
-         /* We must deal with the binfo's directly as a direct base
-            might be inaccessible due to ambiguity.  */
-         tree binfo = TREE_VEC_ELT (binfos, i);
-         tree src = build_base_path (PLUS_EXPR, parm, binfo, 1);
-         tree dst = build_base_path (PLUS_EXPR, current_class_ref, binfo, 1);
-
-         tree expr = build_method_call (dst,
-                                        ansi_assopname (NOP_EXPR),
-                                        build_tree_list (NULL_TREE, src),
-                                        binfo,
-                                        LOOKUP_NORMAL | LOOKUP_NONVIRTUAL);
-         finish_expr_stmt (expr);
+         tree converted_parm;
+
+         /* We must convert PARM directly to the base class
+            explicitly since the base class may be ambiguous.  */
+         converted_parm = build_base_path (PLUS_EXPR, parm, base_binfo, 1);
+         /* Call the base class assignment operator.  */
+         finish_expr_stmt
+           (build_special_member_call (current_class_ref,
+                                       ansi_assopname (NOP_EXPR),
+                                       build_tree_list (NULL_TREE,
+                                                        converted_parm),
+                                       base_binfo,
+                                       LOOKUP_NORMAL | LOOKUP_NONVIRTUAL,
+                                        tf_warning_or_error));
        }
-      for (; fields; fields = TREE_CHAIN (fields))
+
+      /* Assign to each of the non-static data members.  */
+      for (fields = TYPE_FIELDS (current_class_type);
+          fields;
+          fields = TREE_CHAIN (fields))
        {
-         tree comp, init, t;
+         tree comp = current_class_ref;
+         tree init = parm;
          tree field = fields;
+         tree expr_type;
+         int quals;
 
-         if (TREE_CODE (field) != FIELD_DECL)
+         if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
            continue;
 
-         if (CP_TYPE_CONST_P (TREE_TYPE (field)))
+         expr_type = TREE_TYPE (field);
+
+         if (CP_TYPE_CONST_P (expr_type))
            {
-              error ("non-static const member `%#D', can't use default assignment operator", field);
+             error ("non-static const member %q#D, can't use default "
+                    "assignment operator", field);
              continue;
            }
-         else if (TREE_CODE (TREE_TYPE (field)) == REFERENCE_TYPE)
+         else if (TREE_CODE (expr_type) == REFERENCE_TYPE)
            {
-             error ("non-static reference member `%#D', can't use default assignment operator", field);
+             error ("non-static reference member %q#D, can't use "
+                    "default assignment operator", field);
              continue;
            }
 
-         comp = current_class_ref;
-         init = parm;
-
          if (DECL_NAME (field))
            {
              if (VFIELD_NAME_P (DECL_NAME (field)))
                continue;
-
-             /* True for duplicate members.  */
-             if (IDENTIFIER_CLASS_VALUE (DECL_NAME (field)) != field)
-               continue;
            }
-         else if ((t = TREE_TYPE (field)) != NULL_TREE
-                  && ANON_AGGR_TYPE_P (t)
-                  && TYPE_FIELDS (t) != NULL_TREE)
+         else if (ANON_AGGR_TYPE_P (expr_type)
+                  && TYPE_FIELDS (expr_type) != NULL_TREE)
            /* Just use the field; anonymous types can't have
               nontrivial copy ctors or assignment ops.  */;
          else
            continue;
 
-         comp = build (COMPONENT_REF, TREE_TYPE (field), comp, field);
-         init = build (COMPONENT_REF,
-                       cp_build_qualified_type (TREE_TYPE (field), cvquals),
-                       init, field);
+         comp = build3 (COMPONENT_REF, expr_type, comp, field, NULL_TREE);
+
+         /* Compute the type of init->field  */
+         quals = cvquals;
+         if (DECL_MUTABLE_P (field))
+           quals &= ~TYPE_QUAL_CONST;
+         expr_type = cp_build_qualified_type (expr_type, quals);
+
+         init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
 
          if (DECL_NAME (field))
-           finish_expr_stmt (build_modify_expr (comp, NOP_EXPR, init));
+           init = cp_build_modify_expr (comp, NOP_EXPR, init, 
+                                        tf_warning_or_error);
          else
-           finish_expr_stmt (build (MODIFY_EXPR, TREE_TYPE (comp), comp,
-                                    init));
+           init = build2 (MODIFY_EXPR, TREE_TYPE (comp), comp, init);
+         finish_expr_stmt (init);
        }
     }
   finish_return_stmt (current_class_ref);
-  finish_compound_stmt (/*has_no_scope=*/0, compound_stmt);
+  finish_compound_stmt (compound_stmt);
 }
 
+/* Synthesize FNDECL, a non-static member function.   */
+
 void
-synthesize_method (fndecl)
-     tree fndecl;
+synthesize_method (tree fndecl)
 {
-  int nested = (current_function_decl != NULL_TREE);
+  bool nested = (current_function_decl != NULL_TREE);
   tree context = decl_function_context (fndecl);
-  int need_body = 1;
+  bool need_body = true;
   tree stmt;
+  location_t save_input_location = input_location;
+  int error_count = errorcount;
+  int warning_count = warningcount;
 
-  if (at_eof)
-    import_export_decl (fndecl);
+  /* Reset the source location, we might have been previously
+     deferred, and thus have saved where we were first needed.  */
+  DECL_SOURCE_LOCATION (fndecl)
+    = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));
 
   /* If we've been asked to synthesize a clone, just synthesize the
      cloned function instead.  Doing so will automatically fill in the
      body for the clone.  */
   if (DECL_CLONED_FUNCTION_P (fndecl))
-    {
-      synthesize_method (DECL_CLONED_FUNCTION (fndecl));
-      return;
-    }
+    fndecl = DECL_CLONED_FUNCTION (fndecl);
+
+  /* We may be in the middle of deferred access check.  Disable
+     it now.  */
+  push_deferring_access_checks (dk_no_deferred);
 
   if (! context)
     push_to_top_level ();
   else if (nested)
-    push_function_context_to (context);
-
-  /* Put the function definition at the position where it is needed,
-     rather than within the body of the class.  That way, an error
-     during the generation of the implicit body points at the place
-     where the attempt to generate the function occurs, giving the
-     user a hint as to why we are attempting to generate the
-     function. */
-  DECL_SOURCE_LINE (fndecl) = lineno;
-  DECL_SOURCE_FILE (fndecl) = input_filename;
-
-  interface_unknown = 1;
-  start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
-  clear_last_expr ();
+    push_function_context ();
+
+  input_location = DECL_SOURCE_LOCATION (fndecl);
+
+  start_preparsed_function (fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
   stmt = begin_function_body ();
 
   if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
     {
       do_build_assign_ref (fndecl);
-      need_body = 0;
+      need_body = false;
     }
   else if (DECL_CONSTRUCTOR_P (fndecl))
     {
       tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
       if (arg_chain != void_list_node)
        do_build_copy_constructor (fndecl);
-      else if (TYPE_NEEDS_CONSTRUCTING (current_class_type))
+      else
        finish_mem_initializers (NULL_TREE);
     }
 
@@ -761,18 +796,25 @@ synthesize_method (fndecl)
   if (need_body)
     {
       tree compound_stmt;
-      compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
-      finish_compound_stmt (/*has_no_scope=*/0, compound_stmt);
+      compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+      finish_compound_stmt (compound_stmt);
     }
 
   finish_function_body (stmt);
-  expand_body (finish_function (0));
+  expand_or_defer_fn (finish_function (0));
+
+  input_location = save_input_location;
 
-  extract_interface_info ();
   if (! context)
     pop_from_top_level ();
   else if (nested)
-    pop_function_context_from (context);
+    pop_function_context ();
+
+  pop_deferring_access_checks ();
+
+  if (error_count != errorcount || warning_count != warningcount)
+    inform (input_location, "synthesized method %qD first required here ",
+           fndecl);
 }
 
 /* Use EXTRACTOR to locate the relevant function called for each base &
@@ -782,89 +824,82 @@ synthesize_method (fndecl)
    variants yet, so we need to look at the main one.  */
 
 static tree
-synthesize_exception_spec (type, extractor, client)
-     tree type;
-     tree (*extractor) (tree, void *);
-     void *client;
+synthesize_exception_spec (tree type, tree (*extractor) (tree, void*),
+                          void *client)
 {
   tree raises = empty_except_spec;
   tree fields = TYPE_FIELDS (type);
-  int i, n_bases = CLASSTYPE_N_BASECLASSES (type);
-  tree binfos = TYPE_BINFO_BASETYPES (type);
+  tree binfo, base_binfo;
+  int i;
 
-  for (i = 0; i != n_bases; i++)
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
     {
-      tree base = BINFO_TYPE (TREE_VEC_ELT (binfos, i));
-      tree fn = (*extractor) (base, client);
+      tree fn = (*extractor) (BINFO_TYPE (base_binfo), client);
       if (fn)
-        {
-          tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-          
-          raises = merge_exception_specifiers (raises, fn_raises);
-        }
+       {
+         tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+
+         raises = merge_exception_specifiers (raises, fn_raises);
+       }
     }
   for (; fields; fields = TREE_CHAIN (fields))
     {
       tree type = TREE_TYPE (fields);
       tree fn;
-      
-      if (TREE_CODE (fields) != FIELD_DECL)
-        continue;
+
+      if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields))
+       continue;
       while (TREE_CODE (type) == ARRAY_TYPE)
-       type = TREE_TYPE (type);
-      if (TREE_CODE (type) != RECORD_TYPE)
-        continue;
-      
+       type = TREE_TYPE (type);
+      if (!CLASS_TYPE_P (type))
+       continue;
+
       fn = (*extractor) (type, client);
       if (fn)
-        {
-          tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-          
-          raises = merge_exception_specifiers (raises, fn_raises);
-        }
+       {
+         tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+
+         raises = merge_exception_specifiers (raises, fn_raises);
+       }
     }
   return raises;
 }
 
 /* Locate the dtor of TYPE.  */
 
-static tree
-locate_dtor (type, client)
-     tree type;
-     void *client ATTRIBUTE_UNUSED;
+tree
+locate_dtor (tree type, void *client ATTRIBUTE_UNUSED)
 {
-  tree fns;
-  
-  if (!TYPE_HAS_DESTRUCTOR (type))
-    return NULL_TREE;
-  fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type),
-                      CLASSTYPE_DESTRUCTOR_SLOT);
-  return fns;
+  return CLASSTYPE_DESTRUCTORS (type);
 }
 
 /* Locate the default ctor of TYPE.  */
 
-static tree
-locate_ctor (type, client)
-     tree type;
-     void *client ATTRIBUTE_UNUSED;
+tree
+locate_ctor (tree type, void *client ATTRIBUTE_UNUSED)
 {
   tree fns;
-  
+
   if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
     return NULL_TREE;
-  
-  fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type),
-                      CLASSTYPE_CONSTRUCTOR_SLOT);
-  for (; fns; fns = OVL_NEXT (fns))
+
+  /* Call lookup_fnfields_1 to create the constructor declarations, if
+     necessary.  */
+  if (CLASSTYPE_LAZY_DEFAULT_CTOR (type))
+    return lazily_declare_fn (sfk_constructor, type);
+
+  for (fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns))
     {
       tree fn = OVL_CURRENT (fns);
       tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
-      
-      if (sufficient_parms_p (TREE_CHAIN (parms)))
-        return fn;
+
+      parms = skip_artificial_parms_for (fn, parms);
+
+      if (sufficient_parms_p (parms))
+       return fn;
     }
-  return NULL_TREE;
+  gcc_unreachable ();
 }
 
 struct copy_data
@@ -877,28 +912,32 @@ struct copy_data
    points to a COPY_DATA holding the name (NULL for the ctor)
    and desired qualifiers of the source operand.  */
 
-static tree
-locate_copy (type, client_)
-     tree type;
-     void *client_;
+tree
+locate_copy (tree type, void *client_)
 {
   struct copy_data *client = (struct copy_data *)client_;
   tree fns;
-  int ix = -1;
   tree best = NULL_TREE;
-  int excess_p = 0;
-  
+  bool excess_p = false;
+
   if (client->name)
     {
-      if (TYPE_HAS_ASSIGN_REF (type))
-        ix = lookup_fnfields_1 (type, client->name);
+      int ix;
+      ix = lookup_fnfields_1 (type, client->name);
+      if (ix < 0)
+       return NULL_TREE;
+      fns = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), ix);
     }
   else if (TYPE_HAS_INIT_REF (type))
-    ix = CLASSTYPE_CONSTRUCTOR_SLOT;
-  if (ix < 0)
+    {
+      /* If construction of the copy constructor was postponed, create
+        it now.  */
+      if (CLASSTYPE_LAZY_COPY_CTOR (type))
+       lazily_declare_fn (sfk_copy_constructor, type);
+      fns = CLASSTYPE_CONSTRUCTORS (type);
+    }
+  else
     return NULL_TREE;
-  fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), ix);
-  
   for (; fns; fns = OVL_NEXT (fns))
     {
       tree fn = OVL_CURRENT (fns);
@@ -906,29 +945,31 @@ locate_copy (type, client_)
       tree src_type;
       int excess;
       int quals;
-      
-      parms = TREE_CHAIN (parms);
+
+      parms = skip_artificial_parms_for (fn, parms);
       if (!parms)
-        continue;
-      src_type = TREE_VALUE (parms);
-      if (TREE_CODE (src_type) == REFERENCE_TYPE)
-        src_type = TREE_TYPE (src_type);
+       continue;
+      src_type = non_reference (TREE_VALUE (parms));
+
+      if (src_type == error_mark_node)
+        return NULL_TREE;
+
       if (!same_type_ignoring_top_level_qualifiers_p (src_type, type))
-        continue;
+       continue;
       if (!sufficient_parms_p (TREE_CHAIN (parms)))
-        continue;
+       continue;
       quals = cp_type_quals (src_type);
       if (client->quals & ~quals)
-        continue;
+       continue;
       excess = quals & ~client->quals;
       if (!best || (excess_p && !excess))
-        {
-          best = fn;
-          excess_p = excess;
-        }
+       {
+         best = fn;
+         excess_p = excess;
+       }
       else
-        /* Ambiguous */
-        return NULL_TREE;
+       /* Ambiguous */
+       return NULL_TREE;
     }
   return best;
 }
@@ -936,33 +977,58 @@ locate_copy (type, client_)
 /* Implicitly declare the special function indicated by KIND, as a
    member of TYPE.  For copy constructors and assignment operators,
    CONST_P indicates whether these functions should take a const
-   reference argument or a non-const reference.  */
+   reference argument or a non-const reference.  Returns the
+   FUNCTION_DECL for the implicitly declared function.  */
 
-tree
-implicitly_declare_fn (kind, type, const_p)
-     special_function_kind kind;
-     tree type;
-     int const_p;
+static tree
+implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
 {
-  tree declspecs = NULL_TREE;
-  tree fn, args = NULL_TREE;
+  tree fn;
+  tree parameter_types = void_list_node;
+  tree return_type;
+  tree fn_type;
   tree raises = empty_except_spec;
-  int retref = 0;
-  int has_parm = 0;
-  tree name = constructor_name (TYPE_IDENTIFIER (type));
+  tree rhs_parm_type = NULL_TREE;
+  tree this_parm;
+  tree name;
+  HOST_WIDE_INT saved_processing_template_decl;
+
+  /* Because we create declarations for implicitly declared functions
+     lazily, we may be creating the declaration for a member of TYPE
+     while in some completely different context.  However, TYPE will
+     never be a dependent class (because we never want to do lookups
+     for implicitly defined functions in a dependent class).
+     Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
+     because we only create clones for constructors and destructors
+     when not in a template.  */
+  gcc_assert (!dependent_type_p (type));
+  saved_processing_template_decl = processing_template_decl;
+  processing_template_decl = 0;
+
+  type = TYPE_MAIN_VARIANT (type);
+
+  if (targetm.cxx.cdtor_returns_this () && !TYPE_FOR_JAVA (type))
+    {
+      if (kind == sfk_destructor)
+       /* See comment in check_special_function_return_type.  */
+       return_type = build_pointer_type (void_type_node);
+      else
+       return_type = build_pointer_type (type);
+    }
+  else
+    return_type = void_type_node;
 
   switch (kind)
     {
     case sfk_destructor:
       /* Destructor.  */
-      name = build_nt (BIT_NOT_EXPR, name);
-      args = void_list_node;
+      name = constructor_name (type);
       raises = synthesize_exception_spec (type, &locate_dtor, 0);
       break;
 
     case sfk_constructor:
       /* Default constructor.  */
-      args = void_list_node;
+      name = constructor_name (type);
       raises = synthesize_exception_spec (type, &locate_ctor, 0);
       break;
 
@@ -970,58 +1036,149 @@ implicitly_declare_fn (kind, type, const_p)
     case sfk_assignment_operator:
     {
       struct copy_data data;
-      tree argtype = type;
-      
-      has_parm = 1;
+
       data.name = NULL;
       data.quals = 0;
       if (kind == sfk_assignment_operator)
-        {
-          retref = 1;
-          declspecs = build_tree_list (NULL_TREE, type);
+       {
+         return_type = build_reference_type (type);
+         name = ansi_assopname (NOP_EXPR);
+         data.name = name;
+       }
+      else
+       name = constructor_name (type);
 
-          name = ansi_assopname (NOP_EXPR);
-          data.name = name;
-        }
       if (const_p)
-        {
-          data.quals = TYPE_QUAL_CONST;
-          argtype = build_qualified_type (argtype, TYPE_QUAL_CONST);
-        }
-    
-      argtype = build_reference_type (argtype);
-      args = build_tree_list (hash_tree_chain (argtype, NULL_TREE),
-                             get_identifier ("_ctor_arg"));
-      args = tree_cons (NULL_TREE, args, void_list_node);
-      
+       {
+         data.quals = TYPE_QUAL_CONST;
+         rhs_parm_type = build_qualified_type (type, TYPE_QUAL_CONST);
+       }
+      else
+       rhs_parm_type = type;
+      rhs_parm_type = build_reference_type (rhs_parm_type);
+      parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
       raises = synthesize_exception_spec (type, &locate_copy, &data);
       break;
     }
     default:
-      abort ();
+      gcc_unreachable ();
     }
 
-  TREE_PARMLIST (args) = 1;
+  /* Create the function.  */
+  fn_type = build_method_type_directly (type, return_type, parameter_types);
+  if (raises)
+    fn_type = build_exception_variant (fn_type, raises);
+  fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
+  DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
+  if (kind == sfk_constructor || kind == sfk_copy_constructor)
+    DECL_CONSTRUCTOR_P (fn) = 1;
+  else if (kind == sfk_destructor)
+    DECL_DESTRUCTOR_P (fn) = 1;
+  else
+    {
+      DECL_ASSIGNMENT_OPERATOR_P (fn) = 1;
+      SET_OVERLOADED_OPERATOR_CODE (fn, NOP_EXPR);
+    }
+  
+  /* If pointers to member functions use the least significant bit to
+     indicate whether a function is virtual, ensure a pointer
+     to this function will have that bit clear.  */
+  if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
+      && DECL_ALIGN (fn) < 2 * BITS_PER_UNIT)
+    DECL_ALIGN (fn) = 2 * BITS_PER_UNIT;
+
+  /* Create the explicit arguments.  */
+  if (rhs_parm_type)
+    {
+      /* Note that this parameter is *not* marked DECL_ARTIFICIAL; we
+        want its type to be included in the mangled function
+        name.  */
+      DECL_ARGUMENTS (fn) = cp_build_parm_decl (NULL_TREE, rhs_parm_type);
+      TREE_READONLY (DECL_ARGUMENTS (fn)) = 1;
+    }
+  /* Add the "this" parameter.  */
+  this_parm = build_this_parm (fn_type, TYPE_UNQUALIFIED);
+  TREE_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
+  DECL_ARGUMENTS (fn) = this_parm;
+
+  grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL);
+  set_linkage_according_to_type (type, fn);
+  rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
+  DECL_IN_AGGR_P (fn) = 1;
+  DECL_ARTIFICIAL (fn) = 1;
+  DECL_DEFAULTED_FN (fn) = 1;
+  DECL_NOT_REALLY_EXTERN (fn) = 1;
+  DECL_DECLARED_INLINE_P (fn) = 1;
+  gcc_assert (!TREE_USED (fn));
 
-  {
-    tree declarator = make_call_declarator (name, args, NULL_TREE, raises);
-    
-    if (retref)
-      declarator = build_nt (ADDR_EXPR, declarator);
+  /* Restore PROCESSING_TEMPLATE_DECL.  */
+  processing_template_decl = saved_processing_template_decl;
 
-    fn = grokfield (declarator, declspecs, NULL_TREE, NULL_TREE, NULL_TREE);
-    if (has_parm)
-      TREE_USED (FUNCTION_FIRST_USER_PARM (fn)) = 1;
-  }
+  return fn;
+}
 
-  my_friendly_assert (TREE_CODE (fn) == FUNCTION_DECL, 20000408);
+/* Add an implicit declaration to TYPE for the kind of function
+   indicated by SFK.  Return the FUNCTION_DECL for the new implicit
+   declaration.  */
+
+tree
+lazily_declare_fn (special_function_kind sfk, tree type)
+{
+  tree fn;
+  bool const_p;
+
+  /* Figure out whether or not the argument has a const reference
+     type.  */
+  if (sfk == sfk_copy_constructor)
+    const_p = TYPE_HAS_CONST_INIT_REF (type);
+  else if (sfk == sfk_assignment_operator)
+    const_p = TYPE_HAS_CONST_ASSIGN_REF (type);
+  else
+    /* In this case, CONST_P will be ignored.  */
+    const_p = false;
+  /* Declare the function.  */
+  fn = implicitly_declare_fn (sfk, type, const_p);
+  /* A destructor may be virtual.  */
+  if (sfk == sfk_destructor)
+    check_for_override (fn, type);
+  /* Add it to CLASSTYPE_METHOD_VEC.  */
+  add_method (type, fn, NULL_TREE);
+  /* Add it to TYPE_METHODS.  */
+  if (sfk == sfk_destructor
+      && DECL_VIRTUAL_P (fn)
+      && abi_version_at_least (2))
+    /* The ABI requires that a virtual destructor go at the end of the
+       vtable.  */
+    TYPE_METHODS (type) = chainon (TYPE_METHODS (type), fn);
+  else
+    {
+      /* G++ 3.2 put the implicit destructor at the *beginning* of the
+        TYPE_METHODS list, which cause the destructor to be emitted
+        in an incorrect location in the vtable.  */
+      if (warn_abi && DECL_VIRTUAL_P (fn))
+       warning (OPT_Wabi, "vtable layout for class %qT may not be ABI-compliant"
+                "and may change in a future version of GCC due to "
+                "implicit virtual destructor",
+                type);
+      TREE_CHAIN (fn) = TYPE_METHODS (type);
+      TYPE_METHODS (type) = fn;
+    }
+  maybe_add_class_template_decl_list (type, fn, /*friend_p=*/0);
+  if (sfk == sfk_assignment_operator)
+    CLASSTYPE_LAZY_ASSIGNMENT_OP (type) = 0;
+  else
+    {
+      /* Remember that the function has been created.  */
+      if (sfk == sfk_constructor)
+       CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0;
+      else if (sfk == sfk_copy_constructor)
+       CLASSTYPE_LAZY_COPY_CTOR (type) = 0;
+      else if (sfk == sfk_destructor)
+       CLASSTYPE_LAZY_DESTRUCTOR (type) = 0;
+      /* Create appropriate clones.  */
+      clone_function_decl (fn, /*update_method_vec=*/true);
+    }
 
-  DECL_ARTIFICIAL (fn) = 1;
-  DECL_NOT_REALLY_EXTERN (fn) = 1;
-  DECL_DECLARED_INLINE_P (fn) = 1;
-  DECL_INLINE (fn) = 1;
-  defer_fn (fn);
-  
   return fn;
 }
 
@@ -1029,8 +1186,7 @@ implicitly_declare_fn (kind, type, const_p)
    as there are artificial parms in FN.  */
 
 tree
-skip_artificial_parms_for (fn, list)
-     tree fn, list;
+skip_artificial_parms_for (const_tree fn, tree list)
 {
   if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
     list = TREE_CHAIN (list);
@@ -1043,3 +1199,26 @@ skip_artificial_parms_for (fn, list)
     list = TREE_CHAIN (list);
   return list;
 }
+
+/* Given a FUNCTION_DECL FN and a chain LIST, return the number of
+   artificial parms in FN.  */
+
+int
+num_artificial_parms_for (const_tree fn)
+{
+  int count = 0;
+
+  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+    count++;
+  else
+    return 0;
+
+  if (DECL_HAS_IN_CHARGE_PARM_P (fn))
+    count++;
+  if (DECL_HAS_VTT_PARM_P (fn))
+    count++;
+  return count;
+}
+
+
+#include "gt-cp-method.h"