@@ -226,6 +226,34 @@ warn_function_noreturn (tree decl)
true, warned_about, "noreturn");
}
+/* Emit diagnostic if the callers don't use return value.
+ Only to be called for pure/const function */
+
+static void
+warn_function_unused_ret (struct cgraph_node *node)
+{
+ tree decl = node->decl;
+ int flags = flags_from_decl_or_type (decl);
+
+ if (flags & ECF_NORETURN)
+ return;
+
+ if (!(flags & (ECF_CONST | ECF_PURE))
+ || (flags & ECF_LOOPING_CONST_OR_PURE))
+ return;
+
+ for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+ {
+ gcall *g = e->call_stmt;
+ if (g
+ && !VOID_TYPE_P (gimple_call_return_type (g))
+ && gimple_call_lhs (g) == NULL)
+ warning_at (gimple_location (g), OPT_Wunused_value,
+ "Call from %s to %s has no effect",
+ e->caller->name (), e->callee->name ());
+ }
+}
+
/* Return true if we have a function state for NODE. */
static inline bool
@@ -1475,6 +1503,8 @@ propagate_pure_const (void)
/* Inline clones share declaration with their offline copies;
do not modify their declarations since the offline copy may
be different. */
+ bool warn_unused_ret = false;
+
if (!w->global.inlined_to)
switch (this_state)
{
@@ -1482,6 +1512,7 @@ propagate_pure_const (void)
if (!TREE_READONLY (w->decl))
{
warn_function_const (w->decl, !this_looping);
+ warn_unused_ret = true;
if (dump_file)
fprintf (dump_file, "Function found to be %sconst: %s\n",
this_looping ? "looping " : "",
@@ -1496,6 +1527,8 @@ propagate_pure_const (void)
NULL, true);
if (w->set_const_flag (true, this_looping))
{
+ if (warn_unused_ret)
+ warn_function_unused_ret (w);
if (dump_file)
fprintf (dump_file,
"Declaration updated to be %sconst: %s\n",
@@ -1509,6 +1542,7 @@ propagate_pure_const (void)
if (!DECL_PURE_P (w->decl))
{
warn_function_pure (w->decl, !this_looping);
+ warn_unused_ret = true;
if (dump_file)
fprintf (dump_file, "Function found to be %spure: %s\n",
this_looping ? "looping " : "",
@@ -1521,6 +1555,8 @@ propagate_pure_const (void)
NULL, true);
if (w->set_pure_flag (true, this_looping))
{
+ if (warn_unused_ret)
+ warn_function_unused_ret (w);
if (dump_file)
fprintf (dump_file,
"Declaration updated to be %spure: %s\n",
@@ -1808,11 +1844,14 @@ pass_local_pure_const::execute (function *fun)
changed = true;
}
+ bool warn_unused_ret = false;
+
switch (l->pure_const_state)
{
case IPA_CONST:
if (!TREE_READONLY (current_function_decl))
{
+ warn_unused_ret = true;
warn_function_const (current_function_decl, !l->looping);
if (dump_file)
fprintf (dump_file, "Function found to be %sconst: %s\n",
@@ -1828,6 +1867,8 @@ pass_local_pure_const::execute (function *fun)
}
if (!skip && node->set_const_flag (true, l->looping))
{
+ if (warn_unused_ret)
+ warn_function_unused_ret (cgraph_node::get_create (current_function_decl));
if (dump_file)
fprintf (dump_file, "Declaration updated to be %sconst: %s\n",
l->looping ? "looping " : "",
@@ -1840,6 +1881,7 @@ pass_local_pure_const::execute (function *fun)
if (!DECL_PURE_P (current_function_decl))
{
warn_function_pure (current_function_decl, !l->looping);
+ warn_unused_ret = true;
if (dump_file)
fprintf (dump_file, "Function found to be %spure: %s\n",
l->looping ? "looping " : "",
@@ -1854,6 +1896,8 @@ pass_local_pure_const::execute (function *fun)
}
if (!skip && node->set_pure_flag (true, l->looping))
{
+ if (warn_unused_ret)
+ warn_function_unused_ret (cgraph_node::get_create (current_function_decl));
if (dump_file)
fprintf (dump_file, "Declaration updated to be %spure: %s\n",
l->looping ? "looping " : "",
new file mode 100644
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wunused-value" } */
+
+__attribute__((noinline, no_icf))
+int f()
+{
+ return 1;
+}
+
+__attribute__((noinline, no_icf, const))
+int g()
+{
+ return 1;
+}
+
+int foo (void)
+{
+ f (); /* { dg-warning "Call from foo to f has no effect" } */
+ int k = f (); /* { dg-bogus "Call from foo to f has no effect" } */
+ g (); /* { dg-warning "statement with no effect" } */
+ return k;
+}
+