diff mbox

C++ PATCH for c++/57319 (false positive with -Wvirtual-move-assign)

Message ID 519A5679.3030500@redhat.com
State New
Headers show

Commit Message

Jason Merrill May 20, 2013, 4:59 p.m. UTC
In this testcase, even though B has a non-trivial move assignment 
operator, it's only non-trivial because of the vtable pointer, so the 
warning is a false positive.  This patch avoids this false positive by 
checking for a user-provided op= in the vbase or one of its subobjects 
before warning.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 550825de0b1a1aa50760ae63d8eb856f8b93da74
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 20 12:01:48 2013 -0400

    	PR c++/57319
    	* class.c (vbase_has_user_provided_move_assign): New.
    	* method.c (synthesized_method_walk): Check it.
    	* cp-tree.h: Declare it.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index b936ac8..94ae567 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4831,6 +4831,44 @@  type_has_user_provided_default_constructor (tree t)
   return false;
 }
 
+/* TYPE is being used as a virtual base, and has a non-trivial move
+   assignment.  Return true if this is due to there being a user-provided
+   move assignment in TYPE or one of its subobjects; if there isn't, then
+   multiple move assignment can't cause any harm.  */
+
+bool
+vbase_has_user_provided_move_assign (tree type)
+{
+  /* Does the type itself have a user-provided move assignment operator?  */
+  for (tree fns
+	 = lookup_fnfields_slot_nolazy (type, ansi_assopname (NOP_EXPR));
+       fns; fns = OVL_NEXT (fns))
+    {
+      tree fn = OVL_CURRENT (fns);
+      if (move_fn_p (fn) && user_provided_p (fn))
+	return true;
+    }
+
+  /* Do any of its bases?  */
+  tree binfo = TYPE_BINFO (type);
+  tree base_binfo;
+  for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+    if (vbase_has_user_provided_move_assign (BINFO_TYPE (base_binfo)))
+      return true;
+
+  /* Or non-static data members?  */
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) == FIELD_DECL
+	  && CLASS_TYPE_P (TREE_TYPE (field))
+	  && vbase_has_user_provided_move_assign (TREE_TYPE (field)))
+	return true;
+    }
+
+  /* Seems not.  */
+  return false;
+}
+
 /* If default-initialization leaves part of TYPE uninitialized, returns
    a DECL for the field or TYPE itself (DR 253).  */
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a2f59df..6455c69 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5090,6 +5090,7 @@  extern tree in_class_defaulted_default_constructor (tree);
 extern bool user_provided_p			(tree);
 extern bool type_has_user_provided_constructor  (tree);
 extern bool type_has_user_provided_default_constructor (tree);
+extern bool vbase_has_user_provided_move_assign (tree);
 extern tree default_init_uninitialized_part (tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
 extern bool type_has_constexpr_default_constructor (tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 801b3a5..0d779a0 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1353,7 +1353,8 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
       if (diag && assign_p && move_p
 	  && BINFO_VIRTUAL_P (base_binfo)
 	  && rval && TREE_CODE (rval) == FUNCTION_DECL
-	  && move_fn_p (rval) && !trivial_fn_p (rval))
+	  && move_fn_p (rval) && !trivial_fn_p (rval)
+	  && vbase_has_user_provided_move_assign (basetype))
 	warning (OPT_Wvirtual_move_assign,
 		 "defaulted move assignment for %qT calls a non-trivial "
 		 "move assignment operator for virtual base %qT",
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted44.C b/gcc/testsuite/g++.dg/cpp0x/defaulted44.C
new file mode 100644
index 0000000..213c139
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted44.C
@@ -0,0 +1,24 @@ 
+// PR c++/57319
+// { dg-require-effective-target c++11 }
+
+namespace N1 {
+  struct A { };
+  struct B: virtual A { };
+  struct C: virtual B { };
+
+  struct D: C
+  {
+    void operator= (D &);
+  };
+}
+
+namespace N2 {
+  struct A { A& operator=(A&&); };
+  struct B: virtual A { };	// { dg-warning "move assignment" }
+  struct C: virtual B { };	// { dg-warning "move assignment" }
+
+  struct D: C
+  {
+    void operator= (D &);
+  };
+}