]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR c++/55189 (enable -Wreturn-type by default)
authorJason Merrill <jason@redhat.com>
Thu, 23 Jan 2014 18:54:08 +0000 (13:54 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 23 Jan 2014 18:54:08 +0000 (13:54 -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.

From-SVN: r207001

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/warn/Wreturn-type-9.C [new file with mode: 0644]

index c9145e65bcc1405db7750fec0dabfb7d5a1c127f..24282044fee1e2966bcf27ae7473c95e1fcecf8c 100644 (file)
@@ -1,5 +1,19 @@
 2014-01-23  Jason Merrill  <jason@redhat.com>
 
+       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.
+
        * call.c (build_op_delete_call): Use make_tree_vector and
        release_tree_vector.
 
index 96af562f245e7022df0a9b1263513bce96cc6334..ab75db837abafbd31052ec6ee15dc7b6aa125da8 100644 (file)
@@ -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);
index 9918416e552d03aec9872fb45f947e0a45a66731..f4819a6c9cb244b447ffc846303899036f7db300 100644 (file)
@@ -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;
index c02de3bf9edcd681d8aea1838e85c744cbd298a8..e12a5289e031253607b53a5e73d9c1c3ec386478 100644 (file)
@@ -10641,6 +10641,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");
index a91df4cdab2f7b630e674f1191cff01040f5e1e5..930ca292c85b4589f1d0b08ded5cbceacdc791a9 100644 (file)
@@ -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.  */
index bba00f4839aeac0431e177d25a53335af1f4864a..3a8dacad8ce2934d652ae400a5e4d73d0e4fde2e 100644 (file)
@@ -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 (file)
index 0000000..1c4d5b8
--- /dev/null
@@ -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();