Commit 20f18c3c by Jason Merrill Committed by Jason Merrill

re PR c++/55189 (enable -Wreturn-type by default)

	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
parent 928d850f
2014-01-23 Jason Merrill <jason@redhat.com> 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 * call.c (build_op_delete_call): Use make_tree_vector and
release_tree_vector. release_tree_vector.
......
...@@ -1127,6 +1127,7 @@ struct GTY(()) language_function { ...@@ -1127,6 +1127,7 @@ struct GTY(()) language_function {
BOOL_BITFIELD returns_value : 1; BOOL_BITFIELD returns_value : 1;
BOOL_BITFIELD returns_null : 1; BOOL_BITFIELD returns_null : 1;
BOOL_BITFIELD returns_abnormally : 1; BOOL_BITFIELD returns_abnormally : 1;
BOOL_BITFIELD infinite_loop: 1;
BOOL_BITFIELD x_in_function_try_handler : 1; BOOL_BITFIELD x_in_function_try_handler : 1;
BOOL_BITFIELD x_in_base_initializer : 1; BOOL_BITFIELD x_in_base_initializer : 1;
...@@ -1136,6 +1137,9 @@ struct GTY(()) language_function { ...@@ -1136,6 +1137,9 @@ struct GTY(()) language_function {
htab_t GTY((param_is(struct named_label_entry))) x_named_labels; htab_t GTY((param_is(struct named_label_entry))) x_named_labels;
cp_binding_level *bindings; cp_binding_level *bindings;
vec<tree, va_gc> *x_local_names; 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; htab_t GTY((param_is (struct cxx_int_tree_map))) extern_decl_map;
}; };
...@@ -1194,6 +1198,12 @@ struct GTY(()) language_function { ...@@ -1194,6 +1198,12 @@ struct GTY(()) language_function {
#define current_function_returns_abnormally \ #define current_function_returns_abnormally \
cp_function_chain->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. */ /* Nonzero if we are processing a base initializer. Zero elsewhere. */
#define in_base_initializer cp_function_chain->x_in_base_initializer #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, ...@@ -5671,6 +5681,7 @@ extern bool perform_or_defer_access_check (tree, tree, tree,
extern int stmts_are_full_exprs_p (void); extern int stmts_are_full_exprs_p (void);
extern void init_cp_semantics (void); extern void init_cp_semantics (void);
extern tree do_poplevel (tree); extern tree do_poplevel (tree);
extern void break_maybe_infinite_loop (void);
extern void add_decl_expr (tree); extern void add_decl_expr (tree);
extern tree maybe_cleanup_point_expr_void (tree); extern tree maybe_cleanup_point_expr_void (tree);
extern tree finish_expr_stmt (tree); extern tree finish_expr_stmt (tree);
......
...@@ -14000,6 +14000,8 @@ finish_function (int flags) ...@@ -14000,6 +14000,8 @@ finish_function (int flags)
&& !current_function_returns_value && !current_function_returns_null && !current_function_returns_value && !current_function_returns_null
/* Don't complain if we abort or throw. */ /* Don't complain if we abort or throw. */
&& !current_function_returns_abnormally && !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. */ /* Don't complain if we are declared noreturn. */
&& !TREE_THIS_VOLATILE (fndecl) && !TREE_THIS_VOLATILE (fndecl)
&& !DECL_NAME (DECL_RESULT (fndecl)) && !DECL_NAME (DECL_RESULT (fndecl))
...@@ -14064,6 +14066,7 @@ finish_function (int flags) ...@@ -14064,6 +14066,7 @@ finish_function (int flags)
f->x_return_value = NULL; f->x_return_value = NULL;
f->bindings = NULL; f->bindings = NULL;
f->extern_decl_map = NULL; f->extern_decl_map = NULL;
f->infinite_loops = NULL;
} }
/* Clear out the bits we don't need. */ /* Clear out the bits we don't need. */
local_names = NULL; local_names = NULL;
......
...@@ -10641,6 +10641,8 @@ cp_parser_jump_statement (cp_parser* parser) ...@@ -10641,6 +10641,8 @@ cp_parser_jump_statement (cp_parser* parser)
gcc_assert ((in_statement & IN_SWITCH_STMT) gcc_assert ((in_statement & IN_SWITCH_STMT)
|| in_statement == IN_ITERATION_STMT); || in_statement == IN_ITERATION_STMT);
statement = finish_break_stmt (); statement = finish_break_stmt ();
if (in_statement == IN_ITERATION_STMT)
break_maybe_infinite_loop ();
break; break;
case IN_OMP_BLOCK: case IN_OMP_BLOCK:
error_at (token->location, "invalid exit from OpenMP structured block"); error_at (token->location, "invalid exit from OpenMP structured block");
......
...@@ -19670,6 +19670,10 @@ instantiate_decl (tree d, int defer_ok, ...@@ -19670,6 +19670,10 @@ instantiate_decl (tree d, int defer_ok,
so that finish_function knows where we are. */ so that finish_function knows where we are. */
input_location input_location
= DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus; = 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. */ /* We don't need the local specializations any more. */
......
...@@ -486,6 +486,62 @@ push_cleanup (tree decl, tree cleanup, bool eh_only) ...@@ -486,6 +486,62 @@ push_cleanup (tree decl, tree cleanup, bool eh_only)
CLEANUP_BODY (stmt) = push_stmt_list (); 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 /* Begin a conditional that might contain a declaration. When generating
normal code, we want the declaration to appear before the statement normal code, we want the declaration to appear before the statement
containing the conditional. When generating template code, we want the containing the conditional. When generating template code, we want the
...@@ -732,7 +788,9 @@ begin_while_stmt (void) ...@@ -732,7 +788,9 @@ begin_while_stmt (void)
void void
finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep) 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) if (ivdep && cond != error_mark_node)
WHILE_COND (while_stmt) = build2 (ANNOTATE_EXPR, WHILE_COND (while_stmt) = build2 (ANNOTATE_EXPR,
TREE_TYPE (WHILE_COND (while_stmt)), TREE_TYPE (WHILE_COND (while_stmt)),
...@@ -747,6 +805,7 @@ finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep) ...@@ -747,6 +805,7 @@ finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep)
void void
finish_while_stmt (tree while_stmt) finish_while_stmt (tree while_stmt)
{ {
end_maybe_infinite_loop (boolean_true_node);
WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt)); WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
} }
...@@ -757,6 +816,7 @@ tree ...@@ -757,6 +816,7 @@ tree
begin_do_stmt (void) begin_do_stmt (void)
{ {
tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE); tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE);
begin_maybe_infinite_loop (boolean_true_node);
add_stmt (r); add_stmt (r);
DO_BODY (r) = push_stmt_list (); DO_BODY (r) = push_stmt_list ();
return r; return r;
...@@ -784,6 +844,7 @@ void ...@@ -784,6 +844,7 @@ void
finish_do_stmt (tree cond, tree do_stmt, bool ivdep) finish_do_stmt (tree cond, tree do_stmt, bool ivdep)
{ {
cond = maybe_convert_cond (cond); cond = maybe_convert_cond (cond);
end_maybe_infinite_loop (cond);
if (ivdep && cond != error_mark_node) if (ivdep && cond != error_mark_node)
cond = build2 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, cond = build2 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
build_int_cst (integer_type_node, annot_expr_ivdep_kind)); build_int_cst (integer_type_node, annot_expr_ivdep_kind));
...@@ -891,7 +952,9 @@ finish_for_init_stmt (tree for_stmt) ...@@ -891,7 +952,9 @@ finish_for_init_stmt (tree for_stmt)
void void
finish_for_cond (tree cond, tree for_stmt, bool ivdep) 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) if (ivdep && cond != error_mark_node)
FOR_COND (for_stmt) = build2 (ANNOTATE_EXPR, FOR_COND (for_stmt) = build2 (ANNOTATE_EXPR,
TREE_TYPE (FOR_COND (for_stmt)), TREE_TYPE (FOR_COND (for_stmt)),
...@@ -940,6 +1003,8 @@ finish_for_expr (tree expr, tree for_stmt) ...@@ -940,6 +1003,8 @@ finish_for_expr (tree expr, tree for_stmt)
void void
finish_for_stmt (tree for_stmt) finish_for_stmt (tree for_stmt)
{ {
end_maybe_infinite_loop (boolean_true_node);
if (TREE_CODE (for_stmt) == RANGE_FOR_STMT) if (TREE_CODE (for_stmt) == RANGE_FOR_STMT)
RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt)); RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
else else
...@@ -968,6 +1033,8 @@ begin_range_for_stmt (tree scope, tree init) ...@@ -968,6 +1033,8 @@ begin_range_for_stmt (tree scope, tree init)
{ {
tree r; tree r;
begin_maybe_infinite_loop (boolean_false_node);
r = build_stmt (input_location, RANGE_FOR_STMT, r = build_stmt (input_location, RANGE_FOR_STMT,
NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
......
// 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();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment