This patch should make it easier to selectively disable
-Wvirtual-move-assign errors by adding an attribute
for move assignment operators which marks them as handling
duplicate calls.

gcc/cp/ChangeLog:

        * method.cc: Include "attribs.h".
        (synthesized_method_walk): Avoid outputting
        -Wvirtual_move_assign when the base class' move assignment
        operator has the handles_virtual_move_assign attribute.
        * tree.cc
        (handle_handles_virtual_move_assign): Add.
        (cxx_gnu_attributes): Add handles_virtual_move_assign to the
        attribute list.

gcc/ChangeLog:

        * doc/extend.texi (C++-Specific Variable, Function, and Type
        Attributes): Document handles_virtual_move_assign.

gcc/testsuite/ChangeLog:

        * g++.dg/warn/Wvirtual-move-assign-1.C: New test.

Signed-off-by: Owen Avery <powerboat9.ga...@gmail.com>
---
 gcc/cp/method.cc                              |  5 ++-
 gcc/cp/tree.cc                                | 28 ++++++++++++++++
 gcc/doc/extend.texi                           | 13 ++++++++
 .../g++.dg/warn/Wvirtual-move-assign-1.C      | 32 +++++++++++++++++++
 4 files changed, 77 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wvirtual-move-assign-1.C

diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 05c19cf0661..898f05c9b7d 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 #include "intl.h"
 #include "common/common-target.h"
+#include "attribs.h"
 
 static void do_build_copy_assign (tree);
 static void do_build_copy_constructor (tree);
@@ -2949,7 +2950,9 @@ synthesized_method_walk (tree ctype, 
special_function_kind sfk, bool const_p,
          && BINFO_VIRTUAL_P (base_binfo)
          && fn && TREE_CODE (fn) == FUNCTION_DECL
          && move_fn_p (fn) && !trivial_fn_p (fn)
-         && vbase_has_user_provided_move_assign (BINFO_TYPE (base_binfo)))
+         && vbase_has_user_provided_move_assign (BINFO_TYPE (base_binfo))
+         && !lookup_attribute ("handles_virtual_move_assign",
+                               DECL_ATTRIBUTES (fn)))
        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/cp/tree.cc b/gcc/cp/tree.cc
index 5863b6878f0..4efd5121319 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -48,6 +48,8 @@ static tree handle_init_priority_attribute (tree *, tree, 
tree, int, bool *);
 static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
 static tree handle_contract_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *);
+static tree handle_handles_virtual_move_assign (tree *, tree, tree, int,
+                                               bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -5234,6 +5236,8 @@ static const attribute_spec cxx_gnu_attributes[] =
     handle_abi_tag_attribute, NULL },
   { "no_dangling", 0, 1, false, true, false, false,
     handle_no_dangling_attribute, NULL },
+  { "handles_virtual_move_assign", 0, 0, false, false, false, false,
+    handle_handles_virtual_move_assign, NULL },
 };
 
 const scoped_attribute_specs cxx_gnu_attribute_table =
@@ -5565,6 +5569,30 @@ handle_no_dangling_attribute (tree *node, tree name, 
tree args, int,
   return NULL_TREE;
 }
 
+/* Handle a "handles_virtual_move_assign" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+tree
+handle_handles_virtual_move_assign (tree *node, tree name, tree /*args*/,
+                                   int /*flags*/, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL || DECL_CONSTRUCTOR_P (*node)
+      || !move_fn_p (*node))
+    {
+      warning (
+       OPT_Wattributes,
+       "%qE attribute ignored; valid only for move assignment operators",
+       name);
+      *no_add_attrs = true;
+    }
+  else
+    {
+      *no_add_attrs = false;
+    }
+
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 0978c4c41b2..39b3455909d 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -30412,6 +30412,19 @@ decltype(auto) foo(T&& t) @{
 @};
 @end smallexample
 
+@cindex @code{handles_virtual_move_assign} function attribute
+@item handles_virtual_move_assign
+
+If a C++ type has a default move assignment operator and virtually
+inherits from a base class with a non-trivial move assignment operator,
+the default move assignment operator may call the non-trivial assigment
+operator multiple times.  This causes gcc to emit a
+@code{virtual-move-assign} warning, even if the non-trivial assignment
+operator is written to handle this.  This attribute can be used on a
+base class' move assignment operator declaration to indicate that it
+can handle the described situation, and that gcc should avoid emitting
+a warning.
+
 @cindex @code{warn_unused} type attribute
 @item warn_unused
 
diff --git a/gcc/testsuite/g++.dg/warn/Wvirtual-move-assign-1.C 
b/gcc/testsuite/g++.dg/warn/Wvirtual-move-assign-1.C
new file mode 100644
index 00000000000..80f89b0656a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wvirtual-move-assign-1.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wvirtual-move-assign -Wattributes" }
+
+class A
+{
+  int val
+  __attribute__ ((handles_virtual_move_assign)); // { dg-warning "valid only 
for move assignment operators" }
+
+public:
+  explicit A (int val)
+  __attribute__ ((handles_virtual_move_assign)) // { dg-warning "valid only 
for move assignment operators" }
+  : val (val) {}
+
+  A (const A &oth): val (0) {}
+  A &operator= (const A &oth) { return *this; }
+  A (A &&oth): val (oth.val)
+  {
+    oth.val = 0;
+  }
+  A &operator= (A &&oth)
+  __attribute__ ((handles_virtual_move_assign))
+  {
+    val += oth.val;
+    oth.val = 0;
+    return *this;
+  }
+};
+
+class B: virtual A
+{
+public:
+  B &operator= (B &&) = default;
+};
-- 
2.48.1

Reply via email to