OSDN Git Service

* method.c (process_subob_fn): Make sure no_implicit_p is non-null
[pf3gnuchains/gcc-fork.git] / gcc / cp / method.c
index 4e22a20..c4947d1 100644 (file)
@@ -1,7 +1,7 @@
 /* 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, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
    Free Software Foundation, Inc.
    Contributed by Michael Tiemann (tiemann@cygnus.com)
 
@@ -29,16 +29,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "tree.h"
 #include "cp-tree.h"
-#include "rtl.h"
-#include "expr.h"
 #include "output.h"
 #include "flags.h"
 #include "toplev.h"
 #include "tm_p.h"
 #include "target.h"
+#include "common/common-target.h"
 #include "tree-pass.h"
 #include "diagnostic.h"
 #include "cgraph.h"
+#include "gimple.h"
 
 /* Various flags to control the mangling process.  */
 
@@ -58,10 +58,8 @@ enum mangling_flags
 
 typedef enum mangling_flags mangling_flags;
 
-static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
-static void do_build_assign_ref (tree);
+static void do_build_copy_assign (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.  */
@@ -106,7 +104,7 @@ make_thunk (tree function, bool this_adjusting,
   /* 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))
+  for (thunk = DECL_THUNKS (function); thunk; thunk = DECL_CHAIN (thunk))
     if (DECL_THIS_THUNK_P (thunk) == this_adjusting
        && THUNK_FIXED_OFFSET (thunk) == d
        && !virtual_offset == !THUNK_VIRTUAL_OFFSET (thunk)
@@ -142,29 +140,25 @@ make_thunk (tree function, bool this_adjusting,
   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_COMDAT (thunk) = DECL_COMDAT (function);
   DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
+  /* The thunk itself is not a constructor or destructor, even if
+     the thing it is thunking to is.  */
   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_CHAIN (thunk) = DECL_THUNKS (function);
   DECL_THUNKS (function) = thunk;
 
   return thunk;
@@ -196,7 +190,7 @@ finish_thunk (tree thunk)
       tree cov_probe;
 
       for (cov_probe = DECL_THUNKS (function);
-          cov_probe; cov_probe = TREE_CHAIN (cov_probe))
+          cov_probe; cov_probe = DECL_CHAIN (cov_probe))
        if (DECL_NAME (cov_probe) == name)
          {
            gcc_assert (!DECL_THUNKS (thunk));
@@ -210,83 +204,43 @@ finish_thunk (tree thunk)
   SET_DECL_ASSEMBLER_NAME (thunk, name);
 }
 
-/* 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.  */
-
-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 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 (!this_adjusting)
-    /* Adjust the pointer by the constant.  */
-    ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
-                      size_int (fixed_offset));
-
-  return ptr;
-}
-
 static GTY (()) int thunk_labelno;
 
-/* Create a static alias to function.  */
+/* Create a static alias to target.  */
 
 tree
-make_alias_for (tree function, tree newid)
+make_alias_for (tree target, tree newid)
 {
-  tree alias = build_decl (DECL_SOURCE_LOCATION (function),
-                          FUNCTION_DECL, newid, TREE_TYPE (function));
-  DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
+  tree alias = build_decl (DECL_SOURCE_LOCATION (target),
+                          TREE_CODE (target), newid, TREE_TYPE (target));
+  DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (target);
   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_READONLY (alias) = TREE_READONLY (target);
+  TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (target);
   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;
+  if (DECL_LANG_SPECIFIC (alias))
+    {
+      DECL_NOT_REALLY_EXTERN (alias) = 1;
+      DECL_USE_TEMPLATE (alias) = 0;
+      DECL_TEMPLATE_INFO (alias) = NULL;
+    }
   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;
+  if (TREE_CODE (alias) == FUNCTION_DECL)
+    {
+      DECL_SAVED_FUNCTION_DATA (alias) = NULL;
+      DECL_DESTRUCTOR_P (alias) = 0;
+      DECL_CONSTRUCTOR_P (alias) = 0;
+      DECL_PENDING_INLINE_P (alias) = 0;
+      DECL_DECLARED_INLINE_P (alias) = 0;
+      DECL_INITIAL (alias) = error_mark_node;
+      DECL_ARGUMENTS (alias) = copy_list (DECL_ARGUMENTS (target));
+    }
+  else
+    TREE_STATIC (alias) = 1;
   TREE_ADDRESSABLE (alias) = 1;
   TREE_USED (alias) = 1;
   SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
@@ -300,13 +254,20 @@ make_alias_for_thunk (tree function)
   tree alias;
   char buf[256];
 
-  ASM_GENERATE_INTERNAL_LABEL (buf, "LTHUNK", thunk_labelno);
+  targetm.asm_out.generate_internal_label (buf, "LTHUNK", thunk_labelno);
   thunk_labelno++;
 
   alias = make_alias_for (function, get_identifier (buf));
 
   if (!flag_syntax_only)
-    assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
+    {
+      struct cgraph_node *funcn, *aliasn;
+      funcn = cgraph_get_node (function);
+      gcc_checking_assert (funcn);
+      aliasn = cgraph_same_body_alias (funcn, alias, function);
+      DECL_ASSEMBLER_NAME (function);
+      gcc_assert (aliasn != NULL);
+    }
 
   return alias;
 }
@@ -322,6 +283,7 @@ use_thunk (tree thunk_fndecl, bool emit_p)
   tree virtual_offset;
   HOST_WIDE_INT fixed_offset, virtual_value;
   bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);
+  struct cgraph_node *funcn, *thunk_node;
 
   /* We should have called finish_thunk to give it a name.  */
   gcc_assert (DECL_NAME (thunk_fndecl));
@@ -377,12 +339,13 @@ use_thunk (tree thunk_fndecl, bool emit_p)
   DECL_EXTERNAL (thunk_fndecl) = 0;
   /* The linkage of the function may have changed.  FIXME in linkage
      rewrite.  */
+  gcc_assert (DECL_INTERFACE_KNOWN (function));
   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))
-    make_decl_one_only (thunk_fndecl, cxx_comdat_group (thunk_fndecl));
+  DECL_COMDAT (thunk_fndecl) = DECL_COMDAT (function);
+  DECL_WEAK (thunk_fndecl) = DECL_WEAK (function);
 
   if (flag_syntax_only)
     {
@@ -393,7 +356,7 @@ use_thunk (tree thunk_fndecl, bool emit_p)
   push_to_top_level ();
 
   if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
-      && targetm.have_named_sections)
+      && targetm_common.have_named_sections)
     {
       resolve_unique_section (function, 0, flag_function_sections);
 
@@ -408,52 +371,31 @@ use_thunk (tree thunk_fndecl, bool emit_p)
 
   /* Set up cloned argument trees for the thunk.  */
   t = NULL_TREE;
-  for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
+  for (a = DECL_ARGUMENTS (function); a; a = DECL_CHAIN (a))
     {
       tree x = copy_node (a);
-      TREE_CHAIN (x) = t;
+      DECL_CHAIN (x) = t;
       DECL_CONTEXT (x) = thunk_fndecl;
-      SET_DECL_RTL (x, NULL_RTX);
+      SET_DECL_RTL (x, NULL);
       DECL_HAS_VALUE_EXPR_P (x) = 0;
+      TREE_ADDRESSABLE (x) = 0;
       t = x;
     }
   a = nreverse (t);
   DECL_ARGUMENTS (thunk_fndecl) = a;
+  TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+  funcn = cgraph_get_node (function);
+  gcc_checking_assert (funcn);
+  thunk_node = cgraph_add_thunk (funcn, thunk_fndecl, function,
+                                this_adjusting, fixed_offset, virtual_value,
+                                virtual_offset, alias);
+  if (DECL_ONE_ONLY (function))
+    cgraph_add_to_same_comdat_group (thunk_node, funcn);
 
-  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 (DECL_SOURCE_LOCATION (thunk_fndecl),
-                     RESULT_DECL, 0, integer_type_node);
-      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);
-
-      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;
-      set_cfun (NULL);
-      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
-    }
-  else
+  if (!this_adjusting
+      || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+                                              virtual_value, alias))
     {
-      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
@@ -462,90 +404,97 @@ use_thunk (tree thunk_fndecl, bool emit_p)
       if (varargs_function_p (function))
        error ("generic thunk code fails for method %q#D which uses %<...%>",
               function);
+    }
 
-      DECL_RESULT (thunk_fndecl) = NULL_TREE;
-
-      start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
-      /* We don't bother with a body block for thunks.  */
-
-      /* There's no need to check accessibility inside the thunk body.  */
-      push_deferring_access_checks (dk_no_check);
-
-      t = a;
-      if (this_adjusting)
-       t = thunk_adjust (t, /*this_adjusting=*/1,
-                         fixed_offset, virtual_offset);
+  pop_from_top_level ();
+}
+\f
+/* Code for synthesizing methods which have default semantics defined.  */
 
-      /* Build up the call to the real function.  */
-      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;
+/* True iff CTYPE has a trivial SFK.  */
 
-      if (VOID_TYPE_P (TREE_TYPE (t)))
-       finish_expr_stmt (t);
-      else
-       {
-         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);
-       }
+static bool
+type_has_trivial_fn (tree ctype, special_function_kind sfk)
+{
+  switch (sfk)
+    {
+    case sfk_constructor:
+      return !TYPE_HAS_COMPLEX_DFLT (ctype);
+    case sfk_copy_constructor:
+      return !TYPE_HAS_COMPLEX_COPY_CTOR (ctype);
+    case sfk_move_constructor:
+      return !TYPE_HAS_COMPLEX_MOVE_CTOR (ctype);
+    case sfk_copy_assignment:
+      return !TYPE_HAS_COMPLEX_COPY_ASSIGN (ctype);
+    case sfk_move_assignment:
+      return !TYPE_HAS_COMPLEX_MOVE_ASSIGN (ctype);
+    case sfk_destructor:
+      return !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype);
+    default:
+      gcc_unreachable ();
+    }
+}
 
-      /* Since we want to emit the thunk, we explicitly mark its name as
-        referenced.  */
-      mark_decl_referenced (thunk_fndecl);
+/* Note that CTYPE has a non-trivial SFK even though we previously thought
+   it was trivial.  */
 
-      /* But we don't want debugging information about it.  */
-      DECL_IGNORED_P (thunk_fndecl) = 1;
+static void
+type_set_nontrivial_flag (tree ctype, special_function_kind sfk)
+{
+  switch (sfk)
+    {
+    case sfk_constructor:
+      TYPE_HAS_COMPLEX_DFLT (ctype) = true;
+      return;
+    case sfk_copy_constructor:
+      TYPE_HAS_COMPLEX_COPY_CTOR (ctype) = true;
+      return;
+    case sfk_move_constructor:
+      TYPE_HAS_COMPLEX_MOVE_CTOR (ctype) = true;
+      return;
+    case sfk_copy_assignment:
+      TYPE_HAS_COMPLEX_COPY_ASSIGN (ctype) = true;
+      return;
+    case sfk_move_assignment:
+      TYPE_HAS_COMPLEX_MOVE_ASSIGN (ctype) = true;
+      return;
+    case sfk_destructor:
+      TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype) = true;
+      return;
+    default:
+      gcc_unreachable ();
+    }
+}
 
-      /* Re-enable access control.  */
-      pop_deferring_access_checks ();
+/* True iff FN is a trivial defaulted member function ([cd]tor, op=).  */
 
-      thunk_fndecl = finish_function (0);
-      cgraph_add_new_function (thunk_fndecl, false);
-    }
+bool
+trivial_fn_p (tree fn)
+{
+  if (!DECL_DEFAULTED_FN (fn))
+    return false;
 
-  pop_from_top_level ();
+  /* If fn is a clone, get the primary variant.  */
+  fn = DECL_ORIGIN (fn);
+  return type_has_trivial_fn (DECL_CONTEXT (fn), special_function_p (fn));
 }
-\f
-/* Code for synthesizing methods which have default semantics defined.  */
 
-/* Generate code for default X(X&) constructor.  */
+/* Generate code for default X(X&) or X(X&&) constructor.  */
 
 static void
 do_build_copy_constructor (tree fndecl)
 {
   tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
+  bool move_p = DECL_MOVE_CONSTRUCTOR_P (fndecl);
+  bool trivial = trivial_fn_p (fndecl);
 
   parm = convert_from_reference (parm);
 
-  if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type)
+  if (trivial
       && is_empty_class (current_class_type))
     /* Don't copy the padding byte; it might not have been allocated
        if *this is a base subobject.  */;
-  else if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type))
+  else if (trivial)
     {
       tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm);
       finish_expr_stmt (t);
@@ -557,6 +506,7 @@ do_build_copy_constructor (tree fndecl)
       int cvquals = cp_type_quals (TREE_TYPE (parm));
       int i;
       tree binfo, base_binfo;
+      tree init;
       VEC(tree,gc) *vbases;
 
       /* Initialize all the base-classes with the parameter converted
@@ -567,11 +517,13 @@ do_build_copy_constructor (tree fndecl)
       for (vbases = CLASSTYPE_VBASECLASSES (current_class_type), i = 0;
           VEC_iterate (tree, vbases, i, binfo); i++)
        {
+         init = build_base_path (PLUS_EXPR, parm, binfo, 1,
+                                 tf_warning_or_error);
+         if (move_p)
+           init = move (init);
          member_init_list
            = tree_cons (binfo,
-                        build_tree_list (NULL_TREE,
-                                         build_base_path (PLUS_EXPR, parm,
-                                                          binfo, 1)),
+                        build_tree_list (NULL_TREE, init),
                         member_init_list);
        }
 
@@ -581,17 +533,18 @@ do_build_copy_constructor (tree fndecl)
          if (BINFO_VIRTUAL_P (base_binfo))
            continue;
 
+         init = build_base_path (PLUS_EXPR, parm, base_binfo, 1,
+                                 tf_warning_or_error);
+         if (move_p)
+           init = move (init);
          member_init_list
            = tree_cons (base_binfo,
-                        build_tree_list (NULL_TREE,
-                                         build_base_path (PLUS_EXPR, parm,
-                                                          base_binfo, 1)),
+                        build_tree_list (NULL_TREE, init),
                         member_init_list);
        }
 
-      for (; fields; fields = TREE_CHAIN (fields))
+      for (; fields; fields = DECL_CHAIN (fields))
        {
-         tree init = parm;
          tree field = fields;
          tree expr_type;
 
@@ -606,7 +559,8 @@ do_build_copy_constructor (tree fndecl)
            }
          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.  */;
+              nontrivial copy ctors or assignment ops or this
+              function would be deleted.  */;
          else
            continue;
 
@@ -621,10 +575,13 @@ do_build_copy_constructor (tree fndecl)
 
              if (DECL_MUTABLE_P (field))
                quals &= ~TYPE_QUAL_CONST;
+             quals |= cp_type_quals (expr_type);
              expr_type = cp_build_qualified_type (expr_type, quals);
            }
 
-         init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
+         init = build3 (COMPONENT_REF, expr_type, parm, field, NULL_TREE);
+         if (move_p && TREE_CODE (expr_type) != REFERENCE_TYPE)
+           init = move (init);
          init = build_tree_list (NULL_TREE, init);
 
          member_init_list = tree_cons (field, init, member_init_list);
@@ -634,19 +591,22 @@ do_build_copy_constructor (tree fndecl)
 }
 
 static void
-do_build_assign_ref (tree fndecl)
+do_build_copy_assign (tree fndecl)
 {
-  tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
+  tree parm = DECL_CHAIN (DECL_ARGUMENTS (fndecl));
   tree compound_stmt;
+  bool move_p = move_fn_p (fndecl);
+  bool trivial = trivial_fn_p (fndecl);
+  int flags = LOOKUP_NORMAL | LOOKUP_NONVIRTUAL | LOOKUP_DEFAULTED;
 
   compound_stmt = begin_compound_stmt (0);
   parm = convert_from_reference (parm);
 
-  if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)
+  if (trivial
       && is_empty_class (current_class_type))
     /* Don't copy the padding byte; it might not have been allocated
        if *this is a base subobject.  */;
-  else if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type))
+  else if (trivial)
     {
       tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm);
       finish_expr_stmt (t);
@@ -667,7 +627,10 @@ do_build_assign_ref (tree fndecl)
 
          /* 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);
+         converted_parm = build_base_path (PLUS_EXPR, parm, base_binfo, 1,
+                                           tf_warning_or_error);
+         if (move_p)
+           converted_parm = move (converted_parm);
          /* Call the base class assignment operator.  */
          parmvec = make_tree_vector_single (converted_parm);
          finish_expr_stmt
@@ -675,7 +638,7 @@ do_build_assign_ref (tree fndecl)
                                        ansi_assopname (NOP_EXPR),
                                        &parmvec,
                                        base_binfo,
-                                       LOOKUP_NORMAL | LOOKUP_NONVIRTUAL,
+                                       flags,
                                         tf_warning_or_error));
          release_tree_vector (parmvec);
        }
@@ -683,7 +646,7 @@ do_build_assign_ref (tree fndecl)
       /* Assign to each of the non-static data members.  */
       for (fields = TYPE_FIELDS (current_class_type);
           fields;
-          fields = TREE_CHAIN (fields))
+          fields = DECL_CHAIN (fields))
        {
          tree comp = current_class_ref;
          tree init = parm;
@@ -698,13 +661,13 @@ do_build_assign_ref (tree fndecl)
 
          if (CP_TYPE_CONST_P (expr_type))
            {
-             error ("non-static const member %q#D, can't use default "
+             error ("non-static const member %q#D, can%'t use default "
                     "assignment operator", field);
              continue;
            }
          else if (TREE_CODE (expr_type) == REFERENCE_TYPE)
            {
-             error ("non-static reference member %q#D, can't use "
+             error ("non-static reference member %q#D, can%'t use "
                     "default assignment operator", field);
              continue;
            }
@@ -717,7 +680,8 @@ do_build_assign_ref (tree fndecl)
          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.  */;
+              nontrivial copy ctors or assignment ops or this
+              function would be deleted.  */;
          else
            continue;
 
@@ -730,6 +694,8 @@ do_build_assign_ref (tree fndecl)
          expr_type = cp_build_qualified_type (expr_type, quals);
 
          init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
+         if (move_p && TREE_CODE (expr_type) != REFERENCE_TYPE)
+           init = move (init);
 
          if (DECL_NAME (field))
            init = cp_build_modify_expr (comp, NOP_EXPR, init, 
@@ -783,7 +749,7 @@ synthesize_method (tree fndecl)
 
   if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
     {
-      do_build_assign_ref (fndecl);
+      do_build_copy_assign (fndecl);
       need_body = false;
     }
   else if (DECL_CONSTRUCTOR_P (fndecl))
@@ -821,161 +787,658 @@ synthesize_method (tree fndecl)
            fndecl);
 }
 
-/* Use EXTRACTOR to locate the relevant function called for each base &
-   class field of TYPE. CLIENT allows additional information to be passed
-   to EXTRACTOR.  Generates the union of all exceptions generated by those
-   functions.  Note that we haven't updated TYPE_FIELDS and such of any
-   variants yet, so we need to look at the main one.  */
+/* Build a reference to type TYPE with cv-quals QUALS, which is an
+   rvalue if RVALUE is true.  */
 
 static tree
-synthesize_exception_spec (tree type, tree (*extractor) (tree, void*),
-                          void *client)
+build_stub_type (tree type, int quals, bool rvalue)
 {
-  tree raises = empty_except_spec;
-  tree fields = TYPE_FIELDS (type);
-  tree binfo, base_binfo;
-  int i;
+  tree argtype = cp_build_qualified_type (type, quals);
+  return cp_build_reference_type (argtype, rvalue);
+}
 
-  for (binfo = TYPE_BINFO (type), i = 0;
-       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
-    {
-      tree fn = (*extractor) (BINFO_TYPE (base_binfo), client);
-      if (fn)
-       {
-         tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+/* Build a dummy glvalue from dereferencing a dummy reference of type
+   REFTYPE.  */
 
-         raises = merge_exception_specifiers (raises, fn_raises);
-       }
-    }
-  for (; fields; fields = TREE_CHAIN (fields))
-    {
-      tree type = TREE_TYPE (fields);
-      tree fn;
+static tree
+build_stub_object (tree reftype)
+{
+  tree stub = build1 (NOP_EXPR, reftype, integer_one_node);
+  return convert_from_reference (stub);
+}
 
-      if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields))
-       continue;
-      while (TREE_CODE (type) == ARRAY_TYPE)
-       type = TREE_TYPE (type);
-      if (!CLASS_TYPE_P (type))
-       continue;
+/* Determine which function will be called when looking up NAME in TYPE,
+   called with a single ARGTYPE argument, or no argument if ARGTYPE is
+   null.  FLAGS and COMPLAIN are as for build_new_method_call.
 
-      fn = (*extractor) (type, client);
-      if (fn)
-       {
-         tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+   Returns a FUNCTION_DECL if all is well.
+   Returns NULL_TREE if overload resolution failed.
+   Returns error_mark_node if the chosen function cannot be called.  */
 
-         raises = merge_exception_specifiers (raises, fn_raises);
-       }
+static tree
+locate_fn_flags (tree type, tree name, tree argtype, int flags,
+                tsubst_flags_t complain)
+{
+  tree ob, fn, fns, binfo, rval;
+  VEC(tree,gc) *args;
+
+  if (TYPE_P (type))
+    binfo = TYPE_BINFO (type);
+  else
+    {
+      binfo = type;
+      type = BINFO_TYPE (binfo);
     }
-  return raises;
+
+  ob = build_stub_object (cp_build_reference_type (type, false));
+  args = make_tree_vector ();
+  if (argtype)
+    {
+      tree arg = build_stub_object (argtype);
+      VEC_quick_push (tree, args, arg);
+    }
+
+  fns = lookup_fnfields (binfo, name, 0);
+  rval = build_new_method_call (ob, fns, &args, binfo, flags, &fn, complain);
+
+  release_tree_vector (args);
+  if (fn && rval == error_mark_node)
+    return rval;
+  else
+    return fn;
 }
 
 /* Locate the dtor of TYPE.  */
 
 tree
-locate_dtor (tree type, void *client ATTRIBUTE_UNUSED)
+get_dtor (tree type, tsubst_flags_t complain)
 {
-  return CLASSTYPE_DESTRUCTORS (type);
+  tree fn = locate_fn_flags (type, complete_dtor_identifier, NULL_TREE,
+                            LOOKUP_NORMAL, complain);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
 }
 
 /* Locate the default ctor of TYPE.  */
 
 tree
-locate_ctor (tree type, void *client ATTRIBUTE_UNUSED)
+locate_ctor (tree type)
 {
-  tree fns;
+  tree fn;
 
-  if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
+  push_deferring_access_checks (dk_no_check);
+  fn = locate_fn_flags (type, complete_ctor_identifier, NULL_TREE,
+                       LOOKUP_SPECULATIVE, tf_none);
+  pop_deferring_access_checks ();
+  if (fn == error_mark_node)
     return NULL_TREE;
+  return fn;
+}
 
-  /* Call lookup_fnfields_1 to create the constructor declarations, if
-     necessary.  */
-  if (CLASSTYPE_LAZY_DEFAULT_CTOR (type))
-    return lazily_declare_fn (sfk_constructor, type);
+/* Likewise, but give any appropriate errors.  */
 
-  for (fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns))
-    {
-      tree fn = OVL_CURRENT (fns);
-      tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
+tree
+get_default_ctor (tree type)
+{
+  tree fn = locate_fn_flags (type, complete_ctor_identifier, NULL_TREE,
+                            LOOKUP_NORMAL, tf_warning_or_error);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
+}
 
-      parms = skip_artificial_parms_for (fn, parms);
+/* Locate the copy ctor of TYPE.  */
 
-      if (sufficient_parms_p (parms))
-       return fn;
-    }
-  gcc_unreachable ();
+tree
+get_copy_ctor (tree type, tsubst_flags_t complain)
+{
+  int quals = (TYPE_HAS_CONST_COPY_CTOR (type)
+              ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+  tree argtype = build_stub_type (type, quals, false);
+  tree fn = locate_fn_flags (type, complete_ctor_identifier, argtype,
+                            LOOKUP_NORMAL, complain);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
 }
 
-struct copy_data
+/* Locate the copy assignment operator of TYPE.  */
+
+tree
+get_copy_assign (tree type)
 {
-  tree name;
-  int quals;
-};
+  int quals = (TYPE_HAS_CONST_COPY_ASSIGN (type)
+              ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+  tree argtype = build_stub_type (type, quals, false);
+  tree fn = locate_fn_flags (type, ansi_assopname (NOP_EXPR), argtype,
+                            LOOKUP_NORMAL, tf_warning_or_error);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
+}
 
-/* Locate the copy ctor or copy assignment of TYPE. CLIENT_
-   points to a COPY_DATA holding the name (NULL for the ctor)
-   and desired qualifiers of the source operand.  */
+/* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
+   DELETED_P or give an error message MSG with argument ARG.  */
 
-tree
-locate_copy (tree type, void *client_)
+static void
+process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
+                 bool *deleted_p, bool *constexpr_p, bool *no_implicit_p,
+                 const char *msg, tree arg)
 {
-  struct copy_data *client = (struct copy_data *)client_;
-  tree fns;
-  tree best = NULL_TREE;
-  bool excess_p = false;
+  if (!fn || fn == error_mark_node)
+    goto bad;
 
-  if (client->name)
+  if (spec_p)
     {
-      int ix;
-      ix = lookup_fnfields_1 (type, client->name);
-      if (ix < 0)
-       return NULL_TREE;
-      fns = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), ix);
+      tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+      *spec_p = merge_exception_specifiers (*spec_p, raises, fn);
     }
-  else if (TYPE_HAS_INIT_REF (type))
+
+  if (!trivial_fn_p (fn))
     {
-      /* 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);
+      if (trivial_p)
+       *trivial_p = false;
+      if (TREE_CODE (arg) == FIELD_DECL
+         && TREE_CODE (DECL_CONTEXT (arg)) == UNION_TYPE)
+       {
+         if (deleted_p)
+           *deleted_p = true;
+         if (msg)
+           error ("union member %q+D with non-trivial %qD", arg, fn);
+       }
     }
-  else
-    return NULL_TREE;
-  for (; fns; fns = OVL_NEXT (fns))
+
+  /* Core 1402: A non-trivial copy op suppresses the implicit
+     declaration of the move ctor/op=.  */
+  if (no_implicit_p && move_p && !move_fn_p (fn) && !trivial_fn_p (fn))
+    *no_implicit_p = true;
+
+  if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))
     {
-      tree fn = OVL_CURRENT (fns);
-      tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
-      tree src_type;
-      int excess;
-      int quals;
-
-      parms = skip_artificial_parms_for (fn, parms);
-      if (!parms)
-       continue;
-      src_type = non_reference (TREE_VALUE (parms));
+      *constexpr_p = false;
+      if (msg)
+       {
+         inform (0, "defaulted constructor calls non-constexpr "
+                 "%q+D", fn);
+         explain_invalid_constexpr_fn (fn);
+       }
+    }
 
-      if (src_type == error_mark_node)
-        return NULL_TREE;
+  return;
 
-      if (!same_type_ignoring_top_level_qualifiers_p (src_type, type))
-       continue;
-      if (!sufficient_parms_p (TREE_CHAIN (parms)))
+ bad:
+  if (deleted_p)
+    *deleted_p = true;
+}
+
+/* Subroutine of synthesized_method_walk to allow recursion into anonymous
+   aggregates.  */
+
+static void
+walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
+                  int quals, bool copy_arg_p, bool move_p,
+                  bool assign_p, tree *spec_p, bool *trivial_p,
+                  bool *deleted_p, bool *constexpr_p, bool *no_implicit_p,
+                  const char *msg, int flags, tsubst_flags_t complain)
+{
+  tree field;
+  for (field = fields; field; field = DECL_CHAIN (field))
+    {
+      tree mem_type, argtype, rval;
+
+      if (TREE_CODE (field) != FIELD_DECL
+         || DECL_ARTIFICIAL (field))
        continue;
-      quals = cp_type_quals (src_type);
-      if (client->quals & ~quals)
+
+      mem_type = strip_array_types (TREE_TYPE (field));
+      if (assign_p)
+       {
+         bool bad = true;
+         if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type))
+           {
+             if (msg)
+               error ("non-static const member %q#D, can%'t use default "
+                      "assignment operator", field);
+           }
+         else if (TREE_CODE (mem_type) == REFERENCE_TYPE)
+           {
+             if (msg)
+               error ("non-static reference member %q#D, can%'t use "
+                      "default assignment operator", field);
+           }
+         else
+           bad = false;
+
+         if (bad && deleted_p)
+           *deleted_p = true;
+       }
+      else if (sfk == sfk_constructor)
+       {
+         bool bad;
+
+         if (DECL_INITIAL (field))
+           {
+             if (msg && DECL_INITIAL (field) == error_mark_node)
+               inform (0, "initializer for %q+#D is invalid", field);
+             if (trivial_p)
+               *trivial_p = false;
+#if 0
+             /* Core 1351: If the field has an NSDMI that could throw, the
+                default constructor is noexcept(false).  FIXME this is
+                broken by deferred parsing and 1360 saying we can't lazily
+                declare a non-trivial default constructor.  Also this
+                needs to do deferred instantiation.  Disable until the
+                conflict between 1351 and 1360 is resolved.  */
+             if (spec_p && !expr_noexcept_p (DECL_INITIAL (field), complain))
+               *spec_p = noexcept_false_spec;
+#endif
+
+             /* Don't do the normal processing.  */
+             continue;
+           }
+
+         bad = false;
+         if (CP_TYPE_CONST_P (mem_type)
+             && default_init_uninitialized_part (mem_type))
+           {
+             if (msg)
+               error ("uninitialized non-static const member %q#D",
+                      field);
+             bad = true;
+           }
+         else if (TREE_CODE (mem_type) == REFERENCE_TYPE)
+           {
+             if (msg)
+               error ("uninitialized non-static reference member %q#D",
+                      field);
+             bad = true;
+           }
+
+         if (bad && deleted_p)
+           *deleted_p = true;
+
+         /* For an implicitly-defined default constructor to be constexpr,
+            every member must have a user-provided default constructor or
+            an explicit initializer.  */
+         if (constexpr_p && !CLASS_TYPE_P (mem_type)
+             && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
+           {
+             *constexpr_p = false;
+             if (msg)
+               inform (0, "defaulted default constructor does not "
+                       "initialize %q+#D", field);
+           }
+       }
+
+      if (!CLASS_TYPE_P (mem_type))
        continue;
-      excess = quals & ~client->quals;
-      if (!best || (excess_p && !excess))
+
+      if (ANON_AGGR_TYPE_P (mem_type))
+       {
+         walk_field_subobs (TYPE_FIELDS (mem_type), fnname, sfk, quals,
+                            copy_arg_p, move_p, assign_p, spec_p, trivial_p,
+                            deleted_p, constexpr_p, no_implicit_p,
+                            msg, flags, complain);
+         continue;
+       }
+
+      if (copy_arg_p)
        {
-         best = fn;
-         excess_p = excess;
+         int mem_quals = cp_type_quals (mem_type) | quals;
+         if (DECL_MUTABLE_P (field))
+           mem_quals &= ~TYPE_QUAL_CONST;
+         argtype = build_stub_type (mem_type, mem_quals, move_p);
        }
       else
-       /* Ambiguous */
-       return NULL_TREE;
+       argtype = NULL_TREE;
+
+      rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
+
+      process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
+                       constexpr_p, no_implicit_p, msg, field);
+    }
+}
+
+/* The caller wants to generate an implicit declaration of SFK for CTYPE
+   which is const if relevant and CONST_P is set.  If spec_p, trivial_p and
+   deleted_p are non-null, set their referent appropriately.  If diag is
+   true, we're either being called from maybe_explain_implicit_delete to
+   give errors, or if constexpr_p is non-null, from
+   explain_invalid_constexpr_fn.  */
+
+static void
+synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
+                        tree *spec_p, bool *trivial_p, bool *deleted_p,
+                        bool *constexpr_p, bool *no_implicit_p, bool diag)
+{
+  tree binfo, base_binfo, scope, fnname, rval, argtype;
+  bool move_p, copy_arg_p, assign_p, expected_trivial, check_vdtor;
+  VEC(tree,gc) *vbases;
+  int i, quals, flags;
+  tsubst_flags_t complain;
+  const char *msg;
+  bool ctor_p;
+
+  if (spec_p)
+    *spec_p = (cxx_dialect >= cxx0x ? noexcept_true_spec : empty_except_spec);
+
+  if (no_implicit_p)
+    *no_implicit_p = false;
+
+  if (deleted_p)
+    {
+      /* "The closure type associated with a lambda-expression has a deleted
+        default constructor and a deleted copy assignment operator."
+         This is diagnosed in maybe_explain_implicit_delete.  */
+      if (LAMBDA_TYPE_P (ctype)
+         && (sfk == sfk_constructor
+             || sfk == sfk_copy_assignment))
+       {
+         *deleted_p = true;
+         return;
+       }
+
+      *deleted_p = false;
+    }
+
+  ctor_p = false;
+  assign_p = false;
+  check_vdtor = false;
+  switch (sfk)
+    {
+    case sfk_move_assignment:
+    case sfk_copy_assignment:
+      assign_p = true;
+      fnname = ansi_assopname (NOP_EXPR);
+      break;
+
+    case sfk_destructor:
+      check_vdtor = true;
+      /* The synthesized method will call base dtors, but check complete
+        here to avoid having to deal with VTT.  */
+      fnname = complete_dtor_identifier;
+      break;
+
+    case sfk_constructor:
+    case sfk_move_constructor:
+    case sfk_copy_constructor:
+      ctor_p = true;
+      fnname = complete_ctor_identifier;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* If that user-written default constructor would satisfy the
+     requirements of a constexpr constructor (7.1.5), the
+     implicitly-defined default constructor is constexpr.  */
+  if (constexpr_p)
+    *constexpr_p = ctor_p;
+
+  move_p = false;
+  switch (sfk)
+    {
+    case sfk_constructor:
+    case sfk_destructor:
+      copy_arg_p = false;
+      break;
+
+    case sfk_move_constructor:
+    case sfk_move_assignment:
+      move_p = true;
+    case sfk_copy_constructor:
+    case sfk_copy_assignment:
+      copy_arg_p = true;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  expected_trivial = type_has_trivial_fn (ctype, sfk);
+  if (trivial_p)
+    *trivial_p = expected_trivial;
+
+  /* The TYPE_HAS_COMPLEX_* flags tell us about constraints from base
+     class versions and other properties of the type.  But a subobject
+     class can be trivially copyable and yet have overload resolution
+     choose a template constructor for initialization, depending on
+     rvalueness and cv-quals.  So we can't exit early for copy/move
+     methods in C++0x.  The same considerations apply in C++98/03, but
+     there the definition of triviality does not consider overload
+     resolution, so a constructor can be trivial even if it would otherwise
+     call a non-trivial constructor.  */
+  if (expected_trivial
+      && (!copy_arg_p || cxx_dialect < cxx0x))
+    {
+      if (constexpr_p && sfk == sfk_constructor)
+       {
+         bool cx = trivial_default_constructor_is_constexpr (ctype);
+         *constexpr_p = cx;
+         if (diag && !cx && TREE_CODE (ctype) == UNION_TYPE)
+           /* A trivial constructor doesn't have any NSDMI.  */
+           inform (input_location, "defaulted default constructor does "
+                   "not initialize any non-static data member");
+       }
+      if (!diag)
+       return;
+    }
+
+  ++cp_unevaluated_operand;
+  ++c_inhibit_evaluation_warnings;
+
+  scope = push_scope (ctype);
+
+  if (diag)
+    {
+      flags = LOOKUP_NORMAL|LOOKUP_SPECULATIVE|LOOKUP_DEFAULTED;
+      complain = tf_warning_or_error;
+    }
+  else
+    {
+      flags = LOOKUP_PROTECT|LOOKUP_SPECULATIVE|LOOKUP_DEFAULTED;
+      complain = tf_none;
+    }
+
+  if (const_p)
+    quals = TYPE_QUAL_CONST;
+  else
+    quals = TYPE_UNQUALIFIED;
+  argtype = NULL_TREE;
+
+  if (!diag)
+    msg = NULL;
+  else if (assign_p)
+    msg = ("base %qT does not have a move assignment operator or trivial "
+          "copy assignment operator");
+  else
+    msg = ("base %qT does not have a move constructor or trivial "
+          "copy constructor");
+
+  for (binfo = TYPE_BINFO (ctype), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+    {
+      tree basetype = BINFO_TYPE (base_binfo);
+      if (copy_arg_p)
+       argtype = build_stub_type (basetype, quals, move_p);
+      rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain);
+
+      process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
+                       constexpr_p, no_implicit_p, msg, basetype);
+      if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
+       {
+         /* In a constructor we also need to check the subobject
+            destructors for cleanup of partially constructed objects.  */
+         rval = locate_fn_flags (base_binfo, complete_dtor_identifier,
+                                 NULL_TREE, flags, complain);
+         /* Note that we don't pass down trivial_p; the subobject
+            destructors don't affect triviality of the constructor.  Nor
+            do they affect constexpr-ness (a constant expression doesn't
+            throw) or exception-specification (a throw from one of the
+            dtors would be a double-fault).  */
+         process_subob_fn (rval, false, NULL, NULL,
+                           deleted_p, NULL, NULL, NULL,
+                           basetype);
+       }
+
+      if (check_vdtor && type_has_virtual_destructor (basetype))
+       {
+         rval = locate_fn_flags (ctype, ansi_opname (DELETE_EXPR),
+                                 ptr_type_node, flags, complain);
+         /* Unlike for base ctor/op=/dtor, for operator delete it's fine
+            to have a null rval (no class-specific op delete).  */
+         if (rval && rval == error_mark_node && deleted_p)
+           *deleted_p = true;
+         check_vdtor = false;
+       }
+    }
+
+  vbases = CLASSTYPE_VBASECLASSES (ctype);
+  if (vbases && assign_p && move_p)
+    {
+      /* Should the spec be changed to allow vbases that only occur once?  */
+      if (diag)
+       error ("%qT has virtual bases, default move assignment operator "
+              "cannot be generated", ctype);
+      else if (deleted_p)
+       *deleted_p = true;
+    }
+  else if (!assign_p)
+    {
+      if (diag)
+       msg = ("virtual base %qT does not have a move constructor "
+              "or trivial copy constructor");
+      if (vbases && constexpr_p)
+       *constexpr_p = false;
+      FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo)
+       {
+         tree basetype = BINFO_TYPE (base_binfo);
+         if (copy_arg_p)
+           argtype = build_stub_type (basetype, quals, move_p);
+         rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain);
+
+         process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
+                           constexpr_p, no_implicit_p, msg, basetype);
+         if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
+           {
+             rval = locate_fn_flags (base_binfo, complete_dtor_identifier,
+                                     NULL_TREE, flags, complain);
+             process_subob_fn (rval, false, NULL, NULL,
+                               deleted_p, NULL, NULL, NULL,
+                               basetype);
+           }
+       }
+    }
+  if (!diag)
+    /* Leave msg null. */;
+  else if (assign_p)
+    msg = ("non-static data member %qD does not have a move "
+          "assignment operator or trivial copy assignment operator");
+  else
+    msg = ("non-static data member %qD does not have a move "
+          "constructor or trivial copy constructor");
+  walk_field_subobs (TYPE_FIELDS (ctype), fnname, sfk, quals,
+                    copy_arg_p, move_p, assign_p, spec_p, trivial_p,
+                    deleted_p, constexpr_p, no_implicit_p,
+                    msg, flags, complain);
+  if (ctor_p)
+    walk_field_subobs (TYPE_FIELDS (ctype), complete_dtor_identifier,
+                      sfk_destructor, TYPE_UNQUALIFIED, false,
+                      false, false, NULL, NULL,
+                      deleted_p, NULL,
+                      NULL, NULL, flags, complain);
+
+  pop_scope (scope);
+
+  --cp_unevaluated_operand;
+  --c_inhibit_evaluation_warnings;
+}
+
+/* DECL is a deleted function.  If it's implicitly deleted, explain why and
+   return true; else return false.  */
+
+bool
+maybe_explain_implicit_delete (tree decl)
+{
+  /* If decl is a clone, get the primary variant.  */
+  decl = DECL_ORIGIN (decl);
+  gcc_assert (DECL_DELETED_FN (decl));
+  if (DECL_DEFAULTED_FN (decl))
+    {
+      /* Not marked GTY; it doesn't need to be GC'd or written to PCH.  */
+      static struct pointer_set_t *explained;
+
+      special_function_kind sfk;
+      location_t loc;
+      bool informed;
+      tree ctype;
+
+      if (!explained)
+       explained = pointer_set_create ();
+      if (pointer_set_insert (explained, decl))
+       return true;
+
+      sfk = special_function_p (decl);
+      ctype = DECL_CONTEXT (decl);
+      loc = input_location;
+      input_location = DECL_SOURCE_LOCATION (decl);
+
+      informed = false;
+      if (LAMBDA_TYPE_P (ctype))
+       {
+         informed = true;
+         if (sfk == sfk_constructor)
+           inform (DECL_SOURCE_LOCATION (decl),
+                   "a lambda closure type has a deleted default constructor");
+         else if (sfk == sfk_copy_assignment)
+           inform (DECL_SOURCE_LOCATION (decl),
+                   "a lambda closure type has a deleted copy assignment operator");
+         else
+           informed = false;
+       }
+      else if (DECL_ARTIFICIAL (decl)
+              && (sfk == sfk_copy_assignment
+                  || sfk == sfk_copy_constructor)
+              && (type_has_user_declared_move_constructor (ctype)
+                  || type_has_user_declared_move_assign (ctype)))
+       {
+         inform (0, "%q+#D is implicitly declared as deleted because %qT "
+                "declares a move constructor or move assignment operator",
+                decl, ctype);
+         informed = true;
+       }
+      if (!informed)
+       {
+         tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (decl));
+         bool const_p = CP_TYPE_CONST_P (non_reference (parm_type));
+         tree scope = push_scope (ctype);
+         inform (0, "%q+#D is implicitly deleted because the default "
+                "definition would be ill-formed:", decl);
+         pop_scope (scope);
+         synthesized_method_walk (ctype, sfk, const_p,
+                                  NULL, NULL, NULL, NULL, NULL, true);
+       }
+
+      input_location = loc;
+      return true;
     }
-  return best;
+  return false;
+}
+
+/* DECL is a defaulted function which was declared constexpr.  Explain why
+   it can't be constexpr.  */
+
+void
+explain_implicit_non_constexpr (tree decl)
+{
+  tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (decl));
+  bool const_p = CP_TYPE_CONST_P (non_reference (parm_type));
+  bool dummy;
+  synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
+                          special_function_p (decl), const_p,
+                          NULL, NULL, NULL, &dummy, NULL, true);
 }
 
 /* Implicitly declare the special function indicated by KIND, as a
@@ -996,6 +1459,10 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
   tree this_parm;
   tree name;
   HOST_WIDE_INT saved_processing_template_decl;
+  bool deleted_p;
+  bool trivial_p;
+  bool constexpr_p;
+  bool no_implicit_p;
 
   /* Because we create declarations for implicitly declared functions
      lazily, we may be creating the declaration for a member of TYPE
@@ -1027,54 +1494,65 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
     case sfk_destructor:
       /* Destructor.  */
       name = constructor_name (type);
-      raises = synthesize_exception_spec (type, &locate_dtor, 0);
       break;
 
     case sfk_constructor:
       /* Default constructor.  */
       name = constructor_name (type);
-      raises = synthesize_exception_spec (type, &locate_ctor, 0);
       break;
 
     case sfk_copy_constructor:
-    case sfk_assignment_operator:
+    case sfk_copy_assignment:
+    case sfk_move_constructor:
+    case sfk_move_assignment:
     {
-      struct copy_data data;
-
-      data.name = NULL;
-      data.quals = 0;
-      if (kind == sfk_assignment_operator)
+      bool move_p;
+      if (kind == sfk_copy_assignment
+         || kind == sfk_move_assignment)
        {
          return_type = build_reference_type (type);
          name = ansi_assopname (NOP_EXPR);
-         data.name = name;
        }
       else
        name = constructor_name (type);
 
       if (const_p)
-       {
-         data.quals = TYPE_QUAL_CONST;
-         rhs_parm_type = build_qualified_type (type, TYPE_QUAL_CONST);
-       }
+       rhs_parm_type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
       else
        rhs_parm_type = type;
-      rhs_parm_type = build_reference_type (rhs_parm_type);
+      move_p = (kind == sfk_move_assignment
+               || kind == sfk_move_constructor);
+      rhs_parm_type = cp_build_reference_type (rhs_parm_type, move_p);
+
       parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
-      raises = synthesize_exception_spec (type, &locate_copy, &data);
       break;
     }
     default:
       gcc_unreachable ();
     }
 
+  synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
+                          &deleted_p, &constexpr_p, &no_implicit_p, false);
+  /* Don't bother marking a deleted constructor as constexpr.  */
+  if (deleted_p)
+    constexpr_p = false;
+  /* A trivial copy/move constructor is also a constexpr constructor.  */
+  else if (trivial_p && cxx_dialect >= cxx0x
+          && (kind == sfk_copy_constructor
+              || kind == sfk_move_constructor))
+    gcc_assert (constexpr_p);
+
+  if (!trivial_p && type_has_trivial_fn (type, kind))
+    type_set_nontrivial_flag (type, kind);
+
   /* 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)
+  if (kind == sfk_constructor || kind == sfk_copy_constructor
+      || kind == sfk_move_constructor)
     DECL_CONSTRUCTOR_P (fn) = 1;
   else if (kind == sfk_destructor)
     DECL_DESTRUCTOR_P (fn) = 1;
@@ -1097,12 +1575,15 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
       /* 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;
+      tree decl = cp_build_parm_decl (NULL_TREE, rhs_parm_type);
+      TREE_READONLY (decl) = 1;
+      retrofit_lang_decl (decl);
+      DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1;
+      DECL_ARGUMENTS (fn) = decl;
     }
   /* Add the "this" parameter.  */
   this_parm = build_this_parm (fn_type, TYPE_UNQUALIFIED);
-  TREE_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
+  DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
   DECL_ARGUMENTS (fn) = this_parm;
 
   grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL);
@@ -1111,6 +1592,13 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
   DECL_IN_AGGR_P (fn) = 1;
   DECL_ARTIFICIAL (fn) = 1;
   DECL_DEFAULTED_FN (fn) = 1;
+  if (cxx_dialect >= cxx0x)
+    {
+      DECL_DELETED_FN (fn) = deleted_p;
+      DECL_DECLARED_CONSTEXPR_P (fn) = constexpr_p;
+    }
+  FNDECL_SUPPRESS_IMPLICIT_DECL (fn) = no_implicit_p;
+  DECL_EXTERNAL (fn) = true;
   DECL_NOT_REALLY_EXTERN (fn) = 1;
   DECL_DECLARED_INLINE_P (fn) = 1;
   gcc_assert (!TREE_USED (fn));
@@ -1121,6 +1609,131 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
   return fn;
 }
 
+/* Gives any errors about defaulted functions which need to be deferred
+   until the containing class is complete.  */
+
+void
+defaulted_late_check (tree fn)
+{
+  /* Complain about invalid signature for defaulted fn.  */
+  tree ctx = DECL_CONTEXT (fn);
+  special_function_kind kind = special_function_p (fn);
+  bool fn_const_p = (copy_fn_p (fn) == 2);
+  tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p);
+
+  if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+                   TREE_TYPE (TREE_TYPE (implicit_fn)))
+      || !compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
+                    TYPE_ARG_TYPES (TREE_TYPE (implicit_fn))))
+    {
+      error ("defaulted declaration %q+D", fn);
+      error_at (DECL_SOURCE_LOCATION (fn),
+               "does not match expected signature %qD", implicit_fn);
+    }
+
+  /* 8.4.2/2: If it is explicitly defaulted on its first declaration, it is
+     implicitly considered to have the same exception-specification as if
+     it had been implicitly declared.  */
+  if (DECL_DEFAULTED_IN_CLASS_P (fn))
+    {
+      tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+      if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
+       {
+         maybe_instantiate_noexcept (fn);
+         if (!comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
+                                 eh_spec, ce_normal))
+           error ("function %q+D defaulted on its first declaration "
+                  "with an exception-specification that differs from "
+                  "the implicit declaration %q#D", fn, implicit_fn);
+       }
+      TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
+      if (DECL_DECLARED_CONSTEXPR_P (implicit_fn))
+       {
+         /* Hmm...should we do this for out-of-class too? Should it be OK to
+            add constexpr later like inline, rather than requiring
+            declarations to match?  */
+         DECL_DECLARED_CONSTEXPR_P (fn) = true;
+         if (kind == sfk_constructor)
+           TYPE_HAS_CONSTEXPR_CTOR (ctx) = true;
+       }
+    }
+
+  if (!DECL_DECLARED_CONSTEXPR_P (implicit_fn)
+      && DECL_DECLARED_CONSTEXPR_P (fn))
+    {
+      if (!CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
+       {
+         error ("explicitly defaulted function %q+D cannot be declared "
+                "as constexpr because the implicit declaration is not "
+                "constexpr:", fn);
+         explain_implicit_non_constexpr (fn);
+       }
+      DECL_DECLARED_CONSTEXPR_P (fn) = false;
+    }
+
+  if (DECL_DELETED_FN (implicit_fn))
+    DECL_DELETED_FN (fn) = 1;
+}
+
+/* Returns true iff FN can be explicitly defaulted, and gives any
+   errors if defaulting FN is ill-formed.  */
+
+bool
+defaultable_fn_check (tree fn)
+{
+  special_function_kind kind = sfk_none;
+
+  if (template_parm_scope_p ())
+    {
+      error ("a template cannot be defaulted");
+      return false;
+    }
+
+  if (DECL_CONSTRUCTOR_P (fn))
+    {
+      if (FUNCTION_FIRST_USER_PARMTYPE (fn) == void_list_node)
+       kind = sfk_constructor;
+      else if (copy_fn_p (fn) > 0
+              && (TREE_CHAIN (FUNCTION_FIRST_USER_PARMTYPE (fn))
+                  == void_list_node))
+       kind = sfk_copy_constructor;
+      else if (move_fn_p (fn))
+       kind = sfk_move_constructor;
+    }
+  else if (DECL_DESTRUCTOR_P (fn))
+    kind = sfk_destructor;
+  else if (DECL_ASSIGNMENT_OPERATOR_P (fn)
+          && DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR)
+    {
+      if (copy_fn_p (fn))
+       kind = sfk_copy_assignment;
+      else if (move_fn_p (fn))
+       kind = sfk_move_assignment;
+    }
+
+  if (kind == sfk_none)
+    {
+      error ("%qD cannot be defaulted", fn);
+      return false;
+    }
+  else
+    {
+      tree t = FUNCTION_FIRST_USER_PARMTYPE (fn);
+      for (; t && t != void_list_node; t = TREE_CHAIN (t))
+       if (TREE_PURPOSE (t))
+         {
+           error ("defaulted function %q+D with default argument", fn);
+           break;
+         }
+      if (TYPE_BEING_DEFINED (DECL_CONTEXT (fn)))
+       /* Defer checking.  */;
+      else if (!processing_template_decl)
+       defaulted_late_check (fn);
+
+      return true;
+    }
+}
+
 /* Add an implicit declaration to TYPE for the kind of function
    indicated by SFK.  Return the FUNCTION_DECL for the new implicit
    declaration.  */
@@ -1129,21 +1742,62 @@ 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;
+  /* Whether or not the argument has a const reference type.  */
+  bool const_p = false;
+
+  switch (sfk)
+    {
+    case sfk_constructor:
+      CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0;
+      break;
+    case sfk_copy_constructor:
+      const_p = TYPE_HAS_CONST_COPY_CTOR (type);
+      CLASSTYPE_LAZY_COPY_CTOR (type) = 0;
+      break;
+    case sfk_move_constructor:
+      CLASSTYPE_LAZY_MOVE_CTOR (type) = 0;
+      break;
+    case sfk_copy_assignment:
+      const_p = TYPE_HAS_CONST_COPY_ASSIGN (type);
+      CLASSTYPE_LAZY_COPY_ASSIGN (type) = 0;
+      break;
+    case sfk_move_assignment:
+      CLASSTYPE_LAZY_MOVE_ASSIGN (type) = 0;
+      break;
+    case sfk_destructor:
+      CLASSTYPE_LAZY_DESTRUCTOR (type) = 0;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
   /* Declare the function.  */
   fn = implicitly_declare_fn (sfk, type, const_p);
+
+  /* [class.copy]/8 If the class definition declares a move constructor or
+     move assignment operator, the implicitly declared copy constructor is
+     defined as deleted.... */
+  if ((sfk == sfk_copy_assignment
+       || sfk == sfk_copy_constructor)
+      && (type_has_user_declared_move_constructor (type)
+         || type_has_user_declared_move_assign (type)))
+    DECL_DELETED_FN (fn) = true;
+
+  /* For move variants, rather than declare them as deleted we just
+     don't declare them at all.  */
+  if (DECL_DELETED_FN (fn)
+      && (sfk == sfk_move_constructor
+         || sfk == sfk_move_assignment))
+    return NULL_TREE;
+
+  /* We also suppress implicit move if it would call a non-trivial copy.  */
+  if (FNDECL_SUPPRESS_IMPLICIT_DECL (fn))
+    return NULL_TREE;
+
   /* A destructor may be virtual.  */
-  if (sfk == sfk_destructor)
+  if (sfk == sfk_destructor
+      || sfk == sfk_move_assignment
+      || sfk == sfk_copy_assignment)
     check_for_override (fn, type);
   /* Add it to CLASSTYPE_METHOD_VEC.  */
   add_method (type, fn, NULL_TREE);
@@ -1159,29 +1813,19 @@ lazily_declare_fn (special_function_kind sfk, tree type)
       /* 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))
+      if (warn_abi && sfk == sfk_destructor && 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);
+      DECL_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);
-    }
+  if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
+      || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
+    /* Create appropriate clones.  */
+    clone_function_decl (fn, /*update_method_vec=*/true);
 
   return fn;
 }