On 4/28/25 5:07 PM, Owen Avery wrote:
As far as I can tell, that would need to be applied to every class which virtually inherits from such a base class, rather than just the base class's move assignment operator.

Ah, sure. But if you replace the lookup_attribute with warning_enabled_at (DECL_SOURCE_LOCATION (fn), OPT_Wvirtual_move_assign)
then disabling the warning around the base op= would be enough.

Jason

On 4/28/25 08:16, Jason Merrill wrote:
On 4/27/25 5:57 PM, Owen Avery wrote:
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.

Thanks, but this sort of situation seems like a good fit for
#pragma GCC diagnostic ignored "-Wvirtual-move-assign"
It's not clear to me that the attribute is a significant usability improvement.

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



Reply via email to