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