commit 4295ec42ce03f2a4283c0e82d5cbdce681a68efa
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jul 10 18:19:28 2012 +0200

    	DR 1402
    	* method.c (synthesized_method_walk): Replace uses of msg with diag.
    	Correct handling of virtual bases with move operations.
    	(process_subob_fn, walk_field_subobs): Replace uses of msg with diag.

diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 79edf81..f3fd7b8 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -923,7 +923,7 @@ get_copy_assign (tree type)
 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)
+		  bool diag, tree arg)
 {
   if (!fn || fn == error_mark_node)
     goto bad;
@@ -943,7 +943,7 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
 	{
 	  if (deleted_p)
 	    *deleted_p = true;
-	  if (msg)
+	  if (diag)
 	    error ("union member %q+D with non-trivial %qD", arg, fn);
 	}
     }
@@ -956,7 +956,7 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
   if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))
     {
       *constexpr_p = false;
-      if (msg)
+      if (diag)
 	{
 	  inform (0, "defaulted constructor calls non-constexpr "
 		  "%q+D", fn);
@@ -979,7 +979,7 @@ 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)
+		   bool diag, int flags, tsubst_flags_t complain)
 {
   tree field;
   for (field = fields; field; field = DECL_CHAIN (field))
@@ -996,13 +996,13 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 	  bool bad = true;
 	  if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type))
 	    {
-	      if (msg)
+	      if (diag)
 		error ("non-static const member %q#D, can%'t use default "
 		       "assignment operator", field);
 	    }
 	  else if (TREE_CODE (mem_type) == REFERENCE_TYPE)
 	    {
-	      if (msg)
+	      if (diag)
 		error ("non-static reference member %q#D, can%'t use "
 		       "default assignment operator", field);
 	    }
@@ -1018,7 +1018,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 
 	  if (DECL_INITIAL (field))
 	    {
-	      if (msg && DECL_INITIAL (field) == error_mark_node)
+	      if (diag && DECL_INITIAL (field) == error_mark_node)
 		inform (0, "initializer for %q+#D is invalid", field);
 	      if (trivial_p)
 		*trivial_p = false;
@@ -1041,14 +1041,14 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 	  if (CP_TYPE_CONST_P (mem_type)
 	      && default_init_uninitialized_part (mem_type))
 	    {
-	      if (msg)
+	      if (diag)
 		error ("uninitialized non-static const member %q#D",
 		       field);
 	      bad = true;
 	    }
 	  else if (TREE_CODE (mem_type) == REFERENCE_TYPE)
 	    {
-	      if (msg)
+	      if (diag)
 		error ("uninitialized non-static reference member %q#D",
 		       field);
 	      bad = true;
@@ -1064,7 +1064,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 	      && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE)
 	    {
 	      *constexpr_p = false;
-	      if (msg)
+	      if (diag)
 		inform (0, "defaulted default constructor does not "
 			"initialize %q+#D", field);
 	    }
@@ -1078,7 +1078,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 	  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);
+			     diag, flags, complain);
 	  continue;
 	}
 
@@ -1095,7 +1095,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
       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);
+			constexpr_p, no_implicit_p, diag, field);
     }
 }
 
@@ -1116,7 +1116,6 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   VEC(tree,gc) *vbases;
   int i, quals, flags;
   tsubst_flags_t complain;
-  const char *msg;
   bool ctor_p;
 
   if (spec_p)
@@ -1240,25 +1239,21 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
     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 (!assign_p && BINFO_VIRTUAL_P (base_binfo))
+	/* We'll handle virtual bases below.  */
+	continue;
+
       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);
+			constexpr_p, no_implicit_p, diag, basetype);
       if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
 	{
 	  /* In a constructor we also need to check the subobject
@@ -1271,7 +1266,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 	     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,
+			    deleted_p, NULL, NULL, false,
 			    basetype);
 	}
 
@@ -1288,21 +1283,31 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
     }
 
   vbases = CLASSTYPE_VBASECLASSES (ctype);
-  if (vbases && assign_p && move_p)
+  if (vbases == NULL)
+    /* No virtual bases to worry about.  */;
+  else if (assign_p && move_p && no_implicit_p)
     {
+      /* Don't implicitly declare a defaulted move assignment if a virtual
+	 base has non-trivial move assignment, since moving the same base
+	 more than once is dangerous.  */
       /* 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;
+      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);
+	  if (rval && rval != error_mark_node
+	      && move_fn_p (rval) && !trivial_fn_p (rval))
+	    {
+	      *no_implicit_p = true;
+	      break;
+	    }
+	}
     }
   else if (!assign_p)
     {
-      if (diag)
-	msg = ("virtual base %qT does not have a move constructor "
-	       "or trivial copy constructor");
-      if (vbases && constexpr_p)
+      if (constexpr_p)
 	*constexpr_p = false;
       FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo)
 	{
@@ -1312,35 +1317,29 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_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);
+			    constexpr_p, no_implicit_p, diag, 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,
+				deleted_p, NULL, NULL, false,
 				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");
+
+  /* Now handle the non-static data members.  */
   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);
+		     diag, 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);
+		       NULL, false, flags, complain);
 
   pop_scope (scope);
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted37.C b/gcc/testsuite/g++.dg/cpp0x/defaulted37.C
new file mode 100644
index 0000000..69105cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted37.C
@@ -0,0 +1,21 @@
+// DR 1402
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  int moved = 0;
+  A& operator=(A&&) { ++moved; }
+  ~A() { if (moved > 1) __builtin_abort(); }
+};
+
+struct B: virtual A { B& operator=(B&&) = default; };
+struct C: virtual A { };	// { dg-error "operator=.const A&" }
+
+int main()
+{
+  B b1, b2;
+  b2 = static_cast<B&&>(b1);
+
+  C c1, c2;
+  c2 = static_cast<C&&>(c1);	// { dg-error "operator=.const C&" }
+}
