@@ -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
@@ -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" }