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.
commit 550825de0b1a1aa50760ae63d8eb856f8b93da74
Author: Jason Merrill <ja...@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 &);
+ };
+}