diff mbox

PR55189 enable -Wreturn-type by default

Message ID 52E163ED.3080300@redhat.com
State New
Headers show

Commit Message

Jason Merrill Jan. 23, 2014, 6:48 p.m. UTC
On 01/16/2014 02:44 PM, Jason Merrill wrote:
> To avoid spurious warnings on code with infinite loops we could add a
> simple check for infinite loops and suppress the warning in that case.
> Basically, if we see a loop with an always-true condition and no breaks.

Like so:

Tested x86_64-pc-linux-gnu, applying to trunk.

Comments

Sylvestre Ledru Jan. 23, 2014, 6:56 p.m. UTC | #1
On 23/01/2014 10:48, Jason Merrill wrote:
> On 01/16/2014 02:44 PM, Jason Merrill wrote:
>> To avoid spurious warnings on code with infinite loops we could add a
>> simple check for infinite loops and suppress the warning in that case.
>> Basically, if we see a loop with an always-true condition and no breaks.
>
> Like so:
>
> Tested x86_64-pc-linux-gnu, applying to trunk.
>
Thanks for that!

S
diff mbox

Patch

commit 9e1ee1aae068265870982c7b615c9e18136ab38a
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jan 16 16:39:58 2014 -0500

    	PR c++/55189
    	* cp-tree.h (struct language_function): Add infinite_loop and
    	infinite_loops.
    	(current_function_infinite_loop): New.
    	* semantics.c (begin_maybe_infinite_loop, end_maybe_infinite_loop)
    	(break_maybe_infinite_loop): New.
    	(finish_while_stmt_cond, finish_while_stmt, begin_do_stmt)
    	(finish_do_stmt, finish_for_cond, finish_for_stmt)
    	(begin_range_for_stmt): Use them.
    	* decl.c (finish_function): Don't warn about missing return
    	if current_function_infinite_loop.
    	* pt.c (instantiate_decl): Copy current_function_infinite_loop.
    	* parser.c (cp_parser_jump_statement): Call break_maybe_infinite_loop.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 96af562f..ab75db8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1127,6 +1127,7 @@  struct GTY(()) language_function {
   BOOL_BITFIELD returns_value : 1;
   BOOL_BITFIELD returns_null : 1;
   BOOL_BITFIELD returns_abnormally : 1;
+  BOOL_BITFIELD infinite_loop: 1;
   BOOL_BITFIELD x_in_function_try_handler : 1;
   BOOL_BITFIELD x_in_base_initializer : 1;
 
@@ -1136,6 +1137,9 @@  struct GTY(()) language_function {
   htab_t GTY((param_is(struct named_label_entry))) x_named_labels;
   cp_binding_level *bindings;
   vec<tree, va_gc> *x_local_names;
+  /* Tracking possibly infinite loops.  This is a vec<tree> only because
+     vec<bool> doesn't work with gtype.  */
+  vec<tree, va_gc> *infinite_loops;
   htab_t GTY((param_is (struct cxx_int_tree_map))) extern_decl_map;
 };
 
@@ -1194,6 +1198,12 @@  struct GTY(()) language_function {
 #define current_function_returns_abnormally \
   cp_function_chain->returns_abnormally
 
+/* Set to 0 at beginning of a function definition, set to 1 if we see an
+   obvious infinite loop.  This can have false positives and false
+   negatives, so it should only be used as a heuristic.  */
+
+#define current_function_infinite_loop cp_function_chain->infinite_loop
+
 /* Nonzero if we are processing a base initializer.  Zero elsewhere.  */
 #define in_base_initializer cp_function_chain->x_in_base_initializer
 
@@ -5671,6 +5681,7 @@  extern bool perform_or_defer_access_check	(tree, tree, tree,
 extern int stmts_are_full_exprs_p		(void);
 extern void init_cp_semantics			(void);
 extern tree do_poplevel				(tree);
+extern void break_maybe_infinite_loop		(void);
 extern void add_decl_expr			(tree);
 extern tree maybe_cleanup_point_expr_void	(tree);
 extern tree finish_expr_stmt			(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 5906653..bfe1daf 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14000,6 +14000,8 @@  finish_function (int flags)
       && !current_function_returns_value && !current_function_returns_null
       /* Don't complain if we abort or throw.  */
       && !current_function_returns_abnormally
+      /* Don't complain if there's an infinite loop.  */
+      && !current_function_infinite_loop
       /* Don't complain if we are declared noreturn.  */
       && !TREE_THIS_VOLATILE (fndecl)
       && !DECL_NAME (DECL_RESULT (fndecl))
@@ -14064,6 +14066,7 @@  finish_function (int flags)
       f->x_return_value = NULL;
       f->bindings = NULL;
       f->extern_decl_map = NULL;
+      f->infinite_loops = NULL;
     }
   /* Clear out the bits we don't need.  */
   local_names = NULL;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 3bc943b..9440df6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -10636,6 +10636,8 @@  cp_parser_jump_statement (cp_parser* parser)
 	  gcc_assert ((in_statement & IN_SWITCH_STMT)
 		      || in_statement == IN_ITERATION_STMT);
 	  statement = finish_break_stmt ();
+	  if (in_statement == IN_ITERATION_STMT)
+	    break_maybe_infinite_loop ();
 	  break;
 	case IN_OMP_BLOCK:
 	  error_at (token->location, "invalid exit from OpenMP structured block");
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2e7cf60..400a143 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -19670,6 +19670,10 @@  instantiate_decl (tree d, int defer_ok,
 	     so that finish_function knows where we are.  */
 	  input_location
 	    = DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus;
+
+	  /* Remember if we saw an infinite loop in the template.  */
+	  current_function_infinite_loop
+	    = DECL_STRUCT_FUNCTION (code_pattern)->language->infinite_loop;
 	}
 
       /* We don't need the local specializations any more.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index eb04266..d911cd5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -486,6 +486,62 @@  push_cleanup (tree decl, tree cleanup, bool eh_only)
   CLEANUP_BODY (stmt) = push_stmt_list ();
 }
 
+/* Simple infinite loop tracking for -Wreturn-type.  We keep a stack of all
+   the current loops, represented by 'NULL_TREE' if we've seen a possible
+   exit, and 'error_mark_node' if not.  This is currently used only to
+   suppress the warning about a function with no return statements, and
+   therefore we don't bother noting returns as possible exits.  We also
+   don't bother with gotos.  */
+
+static void
+begin_maybe_infinite_loop (tree cond)
+{
+  /* Only track this while parsing a function, not during instantiation.  */
+  if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl)
+		&& !processing_template_decl))
+    return;
+  bool maybe_infinite = true;
+  if (cond)
+    {
+      cond = fold_non_dependent_expr (cond);
+      cond = maybe_constant_value (cond);
+      maybe_infinite = integer_nonzerop (cond);
+    }
+  vec_safe_push (cp_function_chain->infinite_loops,
+		 maybe_infinite ? error_mark_node : NULL_TREE);
+
+}
+
+/* A break is a possible exit for the current loop.  */
+
+void
+break_maybe_infinite_loop (void)
+{
+  if (!cfun)
+    return;
+  cp_function_chain->infinite_loops->last() = NULL_TREE;
+}
+
+/* If we reach the end of the loop without seeing a possible exit, we have
+   an infinite loop.  */
+
+static void
+end_maybe_infinite_loop (tree cond)
+{
+  if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl)
+		&& !processing_template_decl))
+    return;
+  tree current = cp_function_chain->infinite_loops->pop();
+  if (current != NULL_TREE)
+    {
+      cond = fold_non_dependent_expr (cond);
+      cond = maybe_constant_value (cond);
+      if (integer_nonzerop (cond))
+	current_function_infinite_loop = 1;
+    }
+}
+
+
 /* Begin a conditional that might contain a declaration.  When generating
    normal code, we want the declaration to appear before the statement
    containing the conditional.  When generating template code, we want the
@@ -732,7 +788,9 @@  begin_while_stmt (void)
 void
 finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep)
 {
-  finish_cond (&WHILE_COND (while_stmt), maybe_convert_cond (cond));
+  cond = maybe_convert_cond (cond);
+  finish_cond (&WHILE_COND (while_stmt), cond);
+  begin_maybe_infinite_loop (cond);
   if (ivdep && cond != error_mark_node)
     WHILE_COND (while_stmt) = build2 (ANNOTATE_EXPR,
 				      TREE_TYPE (WHILE_COND (while_stmt)),
@@ -747,6 +805,7 @@  finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep)
 void
 finish_while_stmt (tree while_stmt)
 {
+  end_maybe_infinite_loop (boolean_true_node);
   WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
 }
 
@@ -757,6 +816,7 @@  tree
 begin_do_stmt (void)
 {
   tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE);
+  begin_maybe_infinite_loop (boolean_true_node);
   add_stmt (r);
   DO_BODY (r) = push_stmt_list ();
   return r;
@@ -784,6 +844,7 @@  void
 finish_do_stmt (tree cond, tree do_stmt, bool ivdep)
 {
   cond = maybe_convert_cond (cond);
+  end_maybe_infinite_loop (cond);
   if (ivdep && cond != error_mark_node)
     cond = build2 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
 		   build_int_cst (integer_type_node, annot_expr_ivdep_kind));
@@ -891,7 +952,9 @@  finish_for_init_stmt (tree for_stmt)
 void
 finish_for_cond (tree cond, tree for_stmt, bool ivdep)
 {
-  finish_cond (&FOR_COND (for_stmt), maybe_convert_cond (cond));
+  cond = maybe_convert_cond (cond);
+  finish_cond (&FOR_COND (for_stmt), cond);
+  begin_maybe_infinite_loop (cond);
   if (ivdep && cond != error_mark_node)
     FOR_COND (for_stmt) = build2 (ANNOTATE_EXPR,
 				  TREE_TYPE (FOR_COND (for_stmt)),
@@ -940,6 +1003,8 @@  finish_for_expr (tree expr, tree for_stmt)
 void
 finish_for_stmt (tree for_stmt)
 {
+  end_maybe_infinite_loop (boolean_true_node);
+
   if (TREE_CODE (for_stmt) == RANGE_FOR_STMT)
     RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
   else
@@ -968,6 +1033,8 @@  begin_range_for_stmt (tree scope, tree init)
 {
   tree r;
 
+  begin_maybe_infinite_loop (boolean_false_node);
+
   r = build_stmt (input_location, RANGE_FOR_STMT,
 		  NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
 
diff --git a/gcc/testsuite/g++.dg/warn/Wreturn-type-9.C b/gcc/testsuite/g++.dg/warn/Wreturn-type-9.C
new file mode 100644
index 0000000..1c4d5b8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wreturn-type-9.C
@@ -0,0 +1,60 @@ 
+// related to PR c++/55189
+// { dg-options "-Wreturn-type" }
+
+int f1()
+{
+  while (true) { }
+}
+int f2()
+{
+  while (true) { break; }
+} // { dg-warning "no return statement" }
+
+int f3()
+{
+  for (;;) {}
+}
+int f4()
+{
+  for (;;) {break;}
+} // { dg-warning "no return statement" }
+
+int f5()
+{
+  do {} while(true);
+}
+int f6()
+{
+  do {break;} while(true);
+} // { dg-warning "no return statement" }
+
+int f7()
+{
+  for(;;)
+    while (true) {break;}
+}
+
+int f8()
+{
+  for(;;)
+    {
+      while (true) {}
+      break;
+    }
+}
+
+template <class T>
+T f9()
+{
+  for(;;) { }
+}
+
+template int f9();
+
+template <class T>
+T f10()
+{
+  for(;;) { break; }
+} // { dg-warning "no return statement" }
+
+template int f10();