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;
+};