Jonathan suggested to handle std::move and std::forward specially for the
purpose of -Wreturn-local-addr, because it's a fairly common mistake to
return a local var by reference.

Now that we have is_std_move_p it's fairly trivial to extend the warning.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-09-05  Marek Polacek  <pola...@redhat.com>

        PR c++/86982, -Wreturn-local-addr and std::move and std::forward.
        * typeck.c (maybe_warn_about_returning_address_of_local): Handle calls
        to std::move or std::forward.
        (is_std_forward_p): New function.

        * g++.dg/warn/Wreturn-local-addr-5.C: New test.

diff --git gcc/cp/typeck.c gcc/cp/typeck.c
index 84cf4c478aa..9fa4c16bf14 100644
--- gcc/cp/typeck.c
+++ gcc/cp/typeck.c
@@ -63,6 +63,8 @@ static tree lookup_destructor (tree, tree, tree, 
tsubst_flags_t);
 static void error_args_num (location_t, tree, bool);
 static int convert_arguments (tree, vec<tree, va_gc> **, tree, int,
                               tsubst_flags_t);
+static bool is_std_move_p (tree);
+static bool is_std_forward_p (tree);
 
 /* Do `exp = require_complete_type (exp);' to make sure exp
    does not have an incomplete type.  (That includes void types.)
@@ -9071,6 +9073,15 @@ maybe_warn_about_returning_address_of_local (tree retval)
       STRIP_NOPS (whats_returned);
     }
 
+  /* As a special case, we handle a call to std::move or std::forward.  */
+  if (TREE_CODE (whats_returned) == CALL_EXPR
+      && (is_std_move_p (whats_returned)
+         || is_std_forward_p (whats_returned)))
+    {
+      tree arg = CALL_EXPR_ARG (whats_returned, 0);
+      return maybe_warn_about_returning_address_of_local (arg);
+    }
+
   if (TREE_CODE (whats_returned) != ADDR_EXPR)
     return false;
   whats_returned = TREE_OPERAND (whats_returned, 0);
@@ -9136,6 +9147,23 @@ decl_in_std_namespace_p (tree decl)
          && DECL_NAMESPACE_STD_P (decl_namespace_context (decl)));
 }
 
+/* Returns true if FN, a CALL_EXPR, is a call to std::forward.  */
+
+static bool
+is_std_forward_p (tree fn)
+{
+  /* std::forward only takes one argument.  */
+  if (call_expr_nargs (fn) != 1)
+    return false;
+
+  tree fndecl = cp_get_callee_fndecl_nofold (fn);
+  if (!decl_in_std_namespace_p (fndecl))
+    return false;
+
+  tree name = DECL_NAME (fndecl);
+  return name && id_equal (name, "forward");
+}
+
 /* Returns true if FN, a CALL_EXPR, is a call to std::move.  */
 
 static bool
diff --git gcc/testsuite/g++.dg/warn/Wreturn-local-addr-5.C 
gcc/testsuite/g++.dg/warn/Wreturn-local-addr-5.C
index e69de29bb2d..76096279a64 100644
--- gcc/testsuite/g++.dg/warn/Wreturn-local-addr-5.C
+++ gcc/testsuite/g++.dg/warn/Wreturn-local-addr-5.C
@@ -0,0 +1,8 @@
+// PR c++/86982
+// { dg-do compile { target c++11 } }
+
+#include <utility>
+
+int&& f() { int i = 0; return std::move(i); } // { dg-warning "reference to 
local variable" }
+int&& g() { int i = 0; return std::forward<int>(i); } // { dg-warning 
"reference to local variable" }
+int&& h() { long l = 0; return std::forward<int>(l); } // { dg-warning 
"reference to temporary" }

Reply via email to