On 05/08/2014 11:18 AM, Richard Biener wrote:
I plan to follow the Java rules, with necessary adjustments due to language
differences:
<http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21>
They are based on syntax (except for the infinite loop case), so they are
much more predictable from a developer perspective.
There are other warnings which benefit greatly from information collected
during optimization, but unreachable code doesn't.
Then I suggest to implement this warning in the frontends - as surely
you'll have differently modified "Java rules" for different frontends.
Go, Java (through ECJ) and possibly Ada already have such diagnostics
anyway.
At the time a frontend calls cgraph_finalize_function () its AST is
supposed to be in GENERIC (+ FE extensions to GENERIC) form
(so for Fortran it's already lowered too much I guess). So that could
be a (kind-of) common point to implement this for C/C++ at least.
It turns out that in C, all conditional control flow is lowered to
COND_EXPR and gotos, too, so I had to hack a prototype into the parser.
With hindsight, C++ would have been the better starting point (despite
the much more complex language) because the pre-GENERIC trees seem to
preserve more control flow statements.
I'm attaching the patch I've been playing with, but I feel that it takes
us into the wrong direction, towards more parser hacks and global state.
The patch implements a single-pass analysis over source code constructs,
implementing the following rules:
* Code after an if statement is unreachable if neither branch
completes normally. (Compile-time constants are not evaluated, just
as in Java.)
* Code after an infinite loop (with a constant condition and no break
statements) is unreachable.
* Code after a return statement is unreachable.
* Code after an Objective-C try/catch/finally statement is unreachable
if the try and catch branches do not complete normally.
* Function starts and (case) labels are always unreachable.
* Loop starts are reachable if the loop contains a label or case
label.
I compiled some code with this patch and, in addition to real issues in
the code being compiled, identified a couple of patterns that need
recognizing and fixing for a production-quality implementation (the
patch has failing test cases for many of these):
* noreturn function calls and expressions need warnings (if there
value is used, but see below for an exception) and trigger
loss of reachability (if used as a statement expression).
* Warn about an unreachable conditional expression in do-while,
with a special case for do-...-while (0).
* An unreachable dummy return in functions not returning void to
suppress warnings from other compilers should not receive a warning.
(A dummy return would have zero constant or null pointer constant
as argument. More involved expressions would still trigger
a warning, and so would returns which aren't the last statement
in a function.)
* A return statement containing a call expression targeting a
noreturn function should not receive a warning (again suppression
of compiler warnings).
* Warnings should not be generated for common assert()-style macros
in unreachable positions.
Expression-related warnings could be implemented on top of the existing
trees, I think (but statement expressions might complicate matters). I
could add more state tracking for the assert() handling, but it would be
so much easier to implement on ASTs with a strong relationship to source
code.
I'm not sure how to proceed. Are there plans to move the C and C++
front ends to more concrete ASTs? Would we be willing to accept a
slight performance hit from that?
--
Florian Weimer / Red Hat Product Security Team
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7aa9e23..d10c3d0 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -777,6 +777,10 @@ Wunknown-pragmas
C ObjC C++ ObjC++ Warning Var(warn_unknown_pragmas) LangEnabledBy(C ObjC C++ ObjC++,Wall, 1, 0)
Warn about unrecognized pragmas
+Wunreachable-code
+C ObjC Warning Var(warn_unreachable_code) LangEnabledBy(C ObjC,Wall, 1, 0)
+Warn about unreachable code
+
Wunsuffixed-float-constants
C ObjC Var(warn_unsuffixed_float_constants) Warning
Warn about unsuffixed float constants
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 3abf6b9..907bdef 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -118,10 +118,6 @@ struct obstack parser_obstack;
static GTY(()) struct stmt_tree_s c_stmt_tree;
-/* State saving variables. */
-tree c_break_label;
-tree c_cont_label;
-
/* A list of decls to be made automatically visible in each file scope. */
static GTY(()) tree visible_builtins;
@@ -7869,11 +7865,8 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
current_function_returns_abnormally = 0;
warn_about_return_type = 0;
c_switch_stack = NULL;
-
- /* Indicate no valid break/continue context by setting these variables
- to some non-null, non-label value. We'll notice and emit the proper
- error message in c_finish_bc_stmt. */
- c_break_label = c_cont_label = size_zero_node;
+ c_loop_state = loop_state ();
+ c_reachability_state.mark_reachable ();
decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, true, NULL,
&attributes, NULL, NULL, DEPRECATED_NORMAL);
@@ -8804,8 +8797,8 @@ c_push_function_context (void)
p->base.x_stmt_tree = c_stmt_tree;
c_stmt_tree.x_cur_stmt_list = vec_safe_copy (c_stmt_tree.x_cur_stmt_list);
- p->x_break_label = c_break_label;
- p->x_cont_label = c_cont_label;
+ p->x_loop_state = c_loop_state;
+ p->x_reachability_state = c_reachability_state;
p->x_switch_stack = c_switch_stack;
p->arg_info = current_function_arg_info;
p->returns_value = current_function_returns_value;
@@ -8844,8 +8837,8 @@ c_pop_function_context (void)
c_stmt_tree = p->base.x_stmt_tree;
p->base.x_stmt_tree.x_cur_stmt_list = NULL;
- c_break_label = p->x_break_label;
- c_cont_label = p->x_cont_label;
+ c_loop_state = p->x_loop_state;
+ c_reachability_state = p->x_reachability_state;
c_switch_stack = p->x_switch_stack;
current_function_arg_info = p->arg_info;
current_function_returns_value = p->returns_value;
diff --git a/gcc/c/c-lang.c b/gcc/c/c-lang.c
index 97c0443..a9e6d94 100644
--- a/gcc/c/c-lang.c
+++ b/gcc/c/c-lang.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-core.h"
#include "c-objc-common.h"
#include "c-family/c-pragma.h"
+#include "c-lang.h"
enum c_language_kind c_language = clk_c;
@@ -48,4 +49,125 @@ enum c_language_kind c_language = clk_c;
/* Each front end provides its own lang hook initializer. */
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+reachability_state c_reachability_state;
+
+void
+reachability_state::warn_unreachable(location_t unreachable_location,
+ const char *description)
+{
+ if (!warn_unreachable_code)
+ return;
+ warning_at (unreachable_location, OPT_Wunreachable_code, "%s", description);
+ switch (state)
+ {
+ case reachable:
+ gcc_unreachable();
+ case after_if:
+ inform (location_of_cause,
+ "neither branches of %<if%> statement completes normally");
+ break;
+ case after_loop:
+ inform (location_of_cause, "loop does not exit normally");
+ break;
+ case after_switch_start:
+ inform (location_of_cause, "switch statement skips initial code");
+ break;
+ case after_goto:
+ inform (location_of_cause,
+ "%<goto%> statement redirects control flow");
+ break;
+ case after_break:
+ inform (location_of_cause,
+ "%<break%> statement redirects control flow");
+ break;
+ case after_continue:
+ inform (location_of_cause,
+ "%<continue%> statement redirects control flow");
+ break;
+ case after_return:
+ inform (location_of_cause,
+ "%<return%> statement redirects control flow");
+ break;
+ case after_call:
+ inform (location_of_cause, "no-return function called");
+ break;
+ case after_throw:
+ inform (location_of_cause,
+ "%<throw%> statement redirects control flow");
+ break;
+ case after_try:
+ inform (location_of_cause,
+ "no branch in %<try%> statement completes normally");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ /* Prevent further warnings. */
+ mark_reachable ();
+}
+
+void
+reachability_state::warn_unreachable (location_t unreachable_loc,
+ enum rid keyword)
+{
+ switch (keyword) {
+ case RID_BREAK:
+ if (c_loop_state.in_switch)
+ return;
+ break;
+ case RID_DO:
+ case RID_FOR:
+ case RID_WHILE:
+ /* Unreachable loops are detected on loop exit. */
+ return;
+ default:
+ /* Fall through. */
+ ;
+ }
+ warn_unreachable (unreachable_loc, "statement is unreachable");
+}
+
+loop_state c_loop_state;
+
+void
+loop_state::enter_switch (loop_state *save)
+{
+ *save = *this;
+ break_label = NULL_TREE;
+ in_switch = true;
+}
+void
+loop_state::enter_loop (loop_state *save)
+{
+ *save = *this;
+ break_label = NULL_TREE;
+ cont_label = NULL_TREE;
+ in_switch = false;
+ at_start = c_reachability_state;
+ c_reachability_state.mark_reachable ();
+}
+
+void
+loop_state::leave_loop (location_t loop_start, const loop_state *save)
+{
+ if (label_seen)
+ {
+ *this = *save;
+ label_seen = true;
+ }
+ else
+ {
+ at_start.warn_if_unreachable
+ (loop_start, "loop statement is unreachable");
+ *this = *save;
+ }
+}
+
+void
+loop_state::leave_switch (const loop_state *save)
+{
+ break_label = save->break_label;
+ in_switch = save->in_switch;
+}
+
#include "gtype-c.h"
diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h
index 7fcf333..8cdbd58 100644
--- a/gcc/c/c-lang.h
+++ b/gcc/c/c-lang.h
@@ -39,14 +39,122 @@ struct GTY((variable_size)) lang_decl {
char dummy;
};
+/* State of the statement reachability analysis in the parser. */
+struct reachability_state
+{
+ /* Location of the construct that caused unreachability. */
+ location_t location_of_cause;
+
+ /* Reason code for unreachability. */
+ enum state {
+ reachable,
+ after_if,
+ after_loop,
+ after_switch_start,
+ after_goto,
+ after_break,
+ after_continue,
+ after_return,
+ after_call,
+ after_throw,
+ after_try
+ } state;
+
+ reachability_state ()
+ {
+ mark_reachable ();
+ }
+
+ /* Marks the current state as reachable. */
+ void mark_reachable ()
+ {
+ state = reachable;
+ location_of_cause = UNKNOWN_LOCATION;
+ }
+
+ /* Marks the state as unreachable, recording the reason. */
+ void mark_unreachable (location_t loc, enum state reason)
+ {
+ state = reason;
+ location_of_cause = loc;
+ }
+
+ /* Returns true if the currrent state is reachable. */
+ bool reachable_p () const
+ {
+ return state == reachable;
+ }
+
+ void warn_if_unreachable (location_t unreachable_location,
+ const char *description)
+ {
+ if (!reachable_p ())
+ warn_unreachable (unreachable_location, description);
+ }
+
+ /* Emits a warning if the statement with the keyword is unreachable,
+ unless it is a "break" in a switch. */
+ void warn_if_unreachable (location_t unreachable_location, enum rid keyword)
+ {
+ if (!reachable_p ())
+ warn_unreachable (unreachable_location, keyword);
+ }
+
+private:
+ void warn_unreachable (location_t, const char *);
+ void warn_unreachable (location_t, enum rid);
+};
+
+/* Loop/switch state in the parser, keeping track of labels used for
+ break/continue statements. */
+struct loop_state
+{
+ tree break_label;
+ tree cont_label;
+ reachability_state at_start;
+ bool in_switch;
+ bool label_seen;
+
+ /* Indicate no valid break/continue context by setting these variables
+ to some non-null, non-label value. We'll notice and emit the proper
+ error message in c_finish_bc_stmt. */
+ loop_state ()
+ : break_label (size_zero_node), cont_label (size_zero_node),
+ in_switch (false), label_seen (false)
+ {
+ }
+
+ /* Enters a loop, resetting the labels, and saves the previous state
+ to *SAVE. */
+ void enter_loop (loop_state *save);
+
+ /* Enters a switch statement, resetting the break label, and saves
+ the previous state to *SAVE. */
+ void enter_switch (loop_state *save);
+
+ /* Leave the loop, keeping the labels seen state. Warn if the loop
+ is unreachable. */
+ void leave_loop (location_t loop_start, const loop_state *save);
+
+ /* Leave the switch statement, restoring the old in-switch status. */
+ void leave_switch (const loop_state *save);
+};
+
+/* The loop state of the construct currently being parsed. */
+extern loop_state c_loop_state;
+
+/* Reachability information for the current function being parsed. */
+extern reachability_state c_reachability_state;
+
+
/* Save and restore the variables in this file and elsewhere
that keep track of the progress of compilation of the current function.
Used for nested functions. */
struct GTY(()) language_function {
struct c_language_function base;
- tree x_break_label;
- tree x_cont_label;
+ struct loop_state GTY((skip)) x_loop_state;
+ struct reachability_state GTY((skip)) x_reachability_state;
struct c_switch * GTY((skip)) x_switch_stack;
struct c_arg_info * GTY((skip)) arg_info;
int returns_value;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 6e8f33b..fbe437f 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -4674,6 +4674,8 @@ c_parser_label (c_parser *parser)
{
location_t loc1 = c_parser_peek_token (parser)->location;
tree label = NULL_TREE;
+ c_reachability_state.mark_reachable ();
+ c_loop_state.label_seen = true;
if (c_parser_next_token_is_keyword (parser, RID_CASE))
{
tree exp1, exp2;
@@ -4869,6 +4871,7 @@ c_parser_statement_after_labels (c_parser *parser)
location_t loc = c_parser_peek_token (parser)->location;
tree stmt = NULL_TREE;
bool in_if_block = parser->in_if_block;
+ enum rid keyword;
parser->in_if_block = false;
switch (c_parser_peek_token (parser)->type)
{
@@ -4876,7 +4879,9 @@ c_parser_statement_after_labels (c_parser *parser)
add_stmt (c_parser_compound_statement (parser));
break;
case CPP_KEYWORD:
- switch (c_parser_peek_token (parser)->keyword)
+ keyword = c_parser_peek_token (parser)->keyword;
+ c_reachability_state.warn_if_unreachable (loc, keyword);
+ switch (keyword)
{
case RID_IF:
c_parser_if_statement (parser);
@@ -4923,11 +4928,11 @@ c_parser_statement_after_labels (c_parser *parser)
goto expect_semicolon;
case RID_CONTINUE:
c_parser_consume_token (parser);
- stmt = c_finish_bc_stmt (loc, &c_cont_label, false);
+ stmt = c_finish_bc_stmt (loc, &c_loop_state.cont_label, false);
goto expect_semicolon;
case RID_BREAK:
c_parser_consume_token (parser);
- stmt = c_finish_bc_stmt (loc, &c_break_label, true);
+ stmt = c_finish_bc_stmt (loc, &c_loop_state.break_label, true);
goto expect_semicolon;
case RID_RETURN:
c_parser_consume_token (parser);
@@ -4962,6 +4967,8 @@ c_parser_statement_after_labels (c_parser *parser)
{
stmt = objc_build_throw_stmt (loc, NULL_TREE);
c_parser_consume_token (parser);
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_throw);
}
else
{
@@ -4969,6 +4976,8 @@ c_parser_statement_after_labels (c_parser *parser)
expr = convert_lvalue_to_rvalue (loc, expr, false, false);
expr.value = c_fully_fold (expr.value, false, NULL);
stmt = objc_build_throw_stmt (loc, expr.value);
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_throw);
goto expect_semicolon;
}
break;
@@ -5001,6 +5010,8 @@ c_parser_statement_after_labels (c_parser *parser)
break;
default:
expr_stmt:
+ c_reachability_state.warn_if_unreachable
+ (loc, "statement expression is unreachable");
stmt = c_finish_expr_stmt (loc, c_parser_expression_conv (parser).value);
expect_semicolon:
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
@@ -5145,6 +5156,7 @@ c_parser_if_statement (c_parser *parser)
tree first_body, second_body;
bool in_if_block;
tree if_stmt;
+ bool fallthrough;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
c_parser_consume_token (parser);
@@ -5154,14 +5166,20 @@ c_parser_if_statement (c_parser *parser)
in_if_block = parser->in_if_block;
parser->in_if_block = true;
first_body = c_parser_if_body (parser, &first_if);
+ fallthrough = c_reachability_state.reachable_p ();
parser->in_if_block = in_if_block;
if (c_parser_next_token_is_keyword (parser, RID_ELSE))
{
c_parser_consume_token (parser);
+ c_reachability_state.mark_reachable ();
second_body = c_parser_else_body (parser);
+ fallthrough |= c_reachability_state.reachable_p ();
}
else
- second_body = NULL_TREE;
+ {
+ second_body = NULL_TREE;
+ fallthrough = true;
+ }
c_finish_if_stmt (loc, cond, first_body, second_body, first_if);
if_stmt = c_end_compound_stmt (loc, block, flag_isoc99);
@@ -5169,6 +5187,11 @@ c_parser_if_statement (c_parser *parser)
if (flag_cilkplus && contains_array_notation_expr (if_stmt))
if_stmt = fix_conditional_array_notations (if_stmt);
add_stmt (if_stmt);
+ if (fallthrough)
+ c_reachability_state.mark_reachable ();
+ else
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_if);
}
/* Parse a switch statement (C90 6.6.4, C99 6.8.4).
@@ -5181,7 +5204,8 @@ static void
c_parser_switch_statement (c_parser *parser)
{
struct c_expr ce;
- tree block, expr, body, save_break;
+ tree block, expr, body;
+ loop_state save_loop;
location_t switch_loc = c_parser_peek_token (parser)->location;
location_t switch_cond_loc;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH));
@@ -5191,6 +5215,8 @@ c_parser_switch_statement (c_parser *parser)
{
switch_cond_loc = c_parser_peek_token (parser)->location;
ce = c_parser_expression (parser);
+ c_reachability_state.warn_if_unreachable
+ (switch_cond_loc, "result of switch condition is unreachable");
ce = convert_lvalue_to_rvalue (switch_cond_loc, ce, true, false);
expr = ce.value;
if (flag_cilkplus && contains_array_notation_expr (expr))
@@ -5208,18 +5234,21 @@ c_parser_switch_statement (c_parser *parser)
expr = error_mark_node;
}
c_start_case (switch_loc, switch_cond_loc, expr);
- save_break = c_break_label;
- c_break_label = NULL_TREE;
+ c_reachability_state.mark_unreachable
+ (switch_loc, reachability_state::after_switch_start);
+ c_loop_state.enter_switch (&save_loop);
body = c_parser_c99_block_statement (parser);
c_finish_case (body);
- if (c_break_label)
+ if (c_loop_state.break_label)
{
location_t here = c_parser_peek_token (parser)->location;
- tree t = build1 (LABEL_EXPR, void_type_node, c_break_label);
+ tree t = build1 (LABEL_EXPR, void_type_node, c_loop_state.break_label);
SET_EXPR_LOCATION (t, here);
add_stmt (t);
}
- c_break_label = save_break;
+ c_loop_state.leave_switch (&save_loop);
+ /* ??? We should recognize if all cases do not complete normally. */
+ c_reachability_state.mark_reachable ();
add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99));
}
@@ -5232,7 +5261,8 @@ c_parser_switch_statement (c_parser *parser)
static void
c_parser_while_statement (c_parser *parser, bool ivdep)
{
- tree block, cond, body, save_break, save_cont;
+ tree block, cond, body;
+ loop_state save_loop;
location_t loc;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE));
c_parser_consume_token (parser);
@@ -5250,15 +5280,12 @@ c_parser_while_statement (c_parser *parser, bool ivdep)
cond = build2 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
build_int_cst (integer_type_node,
annot_expr_ivdep_kind));
- save_break = c_break_label;
- c_break_label = NULL_TREE;
- save_cont = c_cont_label;
- c_cont_label = NULL_TREE;
+ c_loop_state.enter_loop (&save_loop);
body = c_parser_c99_block_statement (parser);
- c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true);
+ c_finish_loop (loc, cond, NULL, body,
+ c_loop_state.break_label, c_loop_state.cont_label, true);
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
- c_break_label = save_break;
- c_cont_label = save_cont;
+ c_loop_state.leave_loop (loc, &save_loop);
}
/* Parse a do statement (C90 6.6.5, C99 6.8.5).
@@ -5270,7 +5297,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep)
static void
c_parser_do_statement (c_parser *parser, bool ivdep)
{
- tree block, cond, body, save_break, save_cont, new_break, new_cont;
+ tree block, cond, body, new_break, new_cont;
+ loop_state save_loop;
location_t loc;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
c_parser_consume_token (parser);
@@ -5280,16 +5308,15 @@ c_parser_do_statement (c_parser *parser, bool ivdep)
"suggest braces around empty body in %<do%> statement");
block = c_begin_compound_stmt (flag_isoc99);
loc = c_parser_peek_token (parser)->location;
- save_break = c_break_label;
- c_break_label = NULL_TREE;
- save_cont = c_cont_label;
- c_cont_label = NULL_TREE;
+ c_loop_state.enter_loop (&save_loop);
body = c_parser_c99_block_statement (parser);
c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>");
- new_break = c_break_label;
- c_break_label = save_break;
- new_cont = c_cont_label;
- c_cont_label = save_cont;
+ new_break = c_loop_state.break_label;
+ new_cont = c_loop_state.cont_label;
+ c_loop_state.leave_loop (loc, &save_loop);
+ /* ??? This is neeeded to support the common do ... while (0)
+ construct. We should recognize that explicitly. */
+ c_reachability_state.mark_reachable ();
cond = c_parser_paren_condition (parser);
if (flag_cilkplus && contains_array_notation_expr (cond))
{
@@ -5366,7 +5393,8 @@ c_parser_do_statement (c_parser *parser, bool ivdep)
static void
c_parser_for_statement (c_parser *parser, bool ivdep)
{
- tree block, cond, incr, save_break, save_cont, body;
+ tree block, cond, incr, body;
+ loop_state save_loop;
/* The following are only used when parsing an ObjC foreach statement. */
tree object_expression;
/* Silence the bogus uninitialized warning. */
@@ -5530,18 +5558,17 @@ c_parser_for_statement (c_parser *parser, bool ivdep)
}
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
}
- save_break = c_break_label;
- c_break_label = NULL_TREE;
- save_cont = c_cont_label;
- c_cont_label = NULL_TREE;
+ c_loop_state.enter_loop (&save_loop);
body = c_parser_c99_block_statement (parser);
if (is_foreach_statement)
- objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
+ objc_finish_foreach_loop
+ (loc, object_expression, collection_expression, body,
+ c_loop_state.break_label, c_loop_state.cont_label);
else
- c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
+ c_finish_loop (loc, cond, incr, body,
+ c_loop_state.break_label, c_loop_state.cont_label, true);
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99 || c_dialect_objc ()));
- c_break_label = save_break;
- c_cont_label = save_cont;
+ c_loop_state.leave_loop (loc, &save_loop);
}
/* Parse an asm statement, a GNU extension. This is a full-blown asm
@@ -8762,15 +8789,17 @@ c_parser_objc_protocol_refs (c_parser *parser)
static void
c_parser_objc_try_catch_finally_statement (c_parser *parser)
{
- location_t location;
+ location_t start_location, location;
tree stmt;
+ bool fallthrough;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_TRY));
c_parser_consume_token (parser);
- location = c_parser_peek_token (parser)->location;
+ start_location = location = c_parser_peek_token (parser)->location;
objc_maybe_warn_exceptions (location);
stmt = c_parser_compound_statement (parser);
objc_begin_try_stmt (location, stmt);
+ fallthrough = c_reachability_state.reachable_p();
while (c_parser_next_token_is_keyword (parser, RID_AT_CATCH))
{
@@ -8779,6 +8808,7 @@ c_parser_objc_try_catch_finally_statement (c_parser *parser)
bool seen_open_paren = false;
c_parser_consume_token (parser);
+ c_reachability_state.mark_reachable ();
if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
seen_open_paren = true;
if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
@@ -8825,15 +8855,24 @@ c_parser_objc_try_catch_finally_statement (c_parser *parser)
if (c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
c_parser_compound_statement_nostart (parser);
objc_finish_catch_clause ();
+ fallthrough |= c_reachability_state.reachable_p ();
}
if (c_parser_next_token_is_keyword (parser, RID_AT_FINALLY))
{
c_parser_consume_token (parser);
+ c_reachability_state.mark_reachable ();
location = c_parser_peek_token (parser)->location;
stmt = c_parser_compound_statement (parser);
objc_build_finally_clause (location, stmt);
+ /* The finally part follows the control flow of the try/catch
+ parts, so it does not affect fallthrough behavior. */
}
objc_finish_try_stmt ();
+ if (fallthrough)
+ c_reachability_state.mark_reachable ();
+ else
+ c_reachability_state.mark_unreachable
+ (start_location, reachability_state::after_try);
}
/* Parse an objc-synchronized-statement.
@@ -11629,7 +11668,8 @@ static tree
c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
tree clauses, tree *cclauses)
{
- tree decl, cond, incr, save_break, save_cont, body, init, stmt, cl;
+ tree decl, cond, incr, body, init, stmt, cl;
+ loop_state save_loop;
tree declv, condv, incrv, initv, ret = NULL;
bool fail = false, open_brace_parsed = false;
int i, collapse = 1, nbraces = 0;
@@ -11804,13 +11844,11 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
nbraces += bracecount;
}
- save_break = c_break_label;
+ c_loop_state.enter_loop (&save_loop);
if (code == CILK_SIMD)
- c_break_label = build_int_cst (size_type_node, 2);
+ c_loop_state.break_label = build_int_cst (size_type_node, 2);
else
- c_break_label = size_one_node;
- save_cont = c_cont_label;
- c_cont_label = NULL_TREE;
+ c_loop_state.break_label = size_one_node;
body = push_stmt_list ();
if (open_brace_parsed)
@@ -11822,16 +11860,15 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
}
else
add_stmt (c_parser_c99_block_statement (parser));
- if (c_cont_label)
+ if (c_loop_state.cont_label)
{
- tree t = build1 (LABEL_EXPR, void_type_node, c_cont_label);
+ tree t = build1 (LABEL_EXPR, void_type_node, c_loop_state.cont_label);
SET_EXPR_LOCATION (t, loc);
add_stmt (t);
}
body = pop_stmt_list (body);
- c_break_label = save_break;
- c_cont_label = save_cont;
+ c_loop_state.leave_loop (loc, &save_loop);
while (nbraces)
{
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a6e7327..71bbff8 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -461,8 +461,6 @@ extern void gen_aux_info_record (tree, int, int, int);
struct c_spot_bindings;
struct c_struct_parse_info;
extern struct obstack parser_obstack;
-extern tree c_break_label;
-extern tree c_cont_label;
extern bool global_bindings_p (void);
extern void push_scope (void);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 7d2df6b..01ea808 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -9116,6 +9116,8 @@ c_finish_goto_label (location_t loc, tree label)
if (!decl)
return NULL_TREE;
TREE_USED (decl) = 1;
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_goto);
{
tree t = build1 (GOTO_EXPR, void_type_node, decl);
SET_EXPR_LOCATION (t, loc);
@@ -9135,6 +9137,8 @@ c_finish_goto_ptr (location_t loc, tree expr)
expr = convert (ptr_type_node, expr);
t = build1 (GOTO_EXPR, void_type_node, expr);
SET_EXPR_LOCATION (t, loc);
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_goto);
return add_stmt (t);
}
@@ -9299,6 +9303,8 @@ c_finish_return (location_t loc, tree retval, tree origtype)
ret_stmt = build_stmt (loc, RETURN_EXPR, retval);
TREE_NO_WARNING (ret_stmt) |= no_warning;
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_return);
return add_stmt (ret_stmt);
}
@@ -9548,6 +9554,7 @@ c_finish_loop (location_t start_locus, tree cond, tree incr, tree body,
tree blab, tree clab, bool cond_is_first)
{
tree entry = NULL, exit = NULL, t;
+ bool fallthrough = false;
if (flag_cilkplus && contains_array_notation_expr (cond))
{
@@ -9565,6 +9572,7 @@ c_finish_loop (location_t start_locus, tree cond, tree incr, tree body,
SET_EXPR_LOCATION (t, start_locus);
add_stmt (t);
}
+ fallthrough = true;
}
else
{
@@ -9600,6 +9608,7 @@ c_finish_loop (location_t start_locus, tree cond, tree incr, tree body,
else
exit = fold_build3_loc (input_location,
COND_EXPR, void_type_node, cond, exit, t);
+ fallthrough = true;
}
add_stmt (top);
@@ -9616,7 +9625,15 @@ c_finish_loop (location_t start_locus, tree cond, tree incr, tree body,
if (exit)
add_stmt (exit);
if (blab)
- add_stmt (build1 (LABEL_EXPR, void_type_node, blab));
+ {
+ add_stmt (build1 (LABEL_EXPR, void_type_node, blab));
+ fallthrough = true;
+ }
+ if (fallthrough)
+ c_reachability_state.mark_reachable ();
+ else
+ c_reachability_state.mark_unreachable
+ (start_locus, reachability_state::after_loop);
}
tree
@@ -9669,8 +9686,15 @@ c_finish_bc_stmt (location_t loc, tree *label_p, bool is_break)
if (skip)
return NULL_TREE;
- if (!is_break)
- add_stmt (build_predict_expr (PRED_CONTINUE, NOT_TAKEN));
+ if (is_break)
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_break);
+ else
+ {
+ c_reachability_state.mark_unreachable
+ (loc, reachability_state::after_continue);
+ add_stmt (build_predict_expr (PRED_CONTINUE, NOT_TAKEN));
+ }
return add_stmt (build1 (GOTO_EXPR, void_type_node, label));
}
diff --git a/gcc/common.opt b/gcc/common.opt
index 5c3f834..afbdbfa 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -663,10 +663,6 @@ Wmaybe-uninitialized
Common Var(warn_maybe_uninitialized) Warning EnabledBy(Wuninitialized)
Warn about maybe uninitialized automatic variables
-Wunreachable-code
-Common Ignore
-Does nothing. Preserved for backward compatibility.
-
Wunused
Common Var(warn_unused) Init(0) Warning
Enable all -Wunused- warnings
diff --git a/gcc/testsuite/gcc.dg/Wunreachable-code-1.c b/gcc/testsuite/gcc.dg/Wunreachable-code-1.c
new file mode 100644
index 0000000..e7b86ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wunreachable-code-1.c
@@ -0,0 +1,103 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunreachable-code" } */
+
+void f(void);
+int g(void);
+
+void jump_into_loop (void)
+{
+ if (1)
+ goto L1;
+ return;
+ while (1) {
+ L1:
+ goto L2;
+ }
+ L2:
+ ;
+}
+
+void switch_header (int i)
+{
+ switch (i) /* { dg-message "note:.*switch.*statement skips initial code" } */
+ {
+ f (); /* { dg-warning "statement expression is unreachable" } */
+ case 1:
+ return;
+ }
+}
+
+void switch_into_loop (int i)
+{
+ switch (i)
+ while (1)
+ {
+ case 1:
+ return;
+ }
+}
+
+int regular_break_in_switch (int i)
+{
+ switch (i)
+ {
+ case 0:
+ break;
+ case 1:
+ return 1;
+ break;
+ case 2:
+ if (i)
+ return 1;
+ else
+ return 2;
+ break;
+ case 3:
+ while (1)
+ {
+ }
+ }
+ return -1;
+}
+
+void unreachable_after_while_loop (void)
+{
+ while (1) /* { dg-message "note: loop does not exit normally" } */
+ f ();
+ f (); /* { dg-warning "statement expression is unreachable" } */
+}
+
+void unreachable_after_do_loop (void)
+{
+ do
+ f (); /* { dg-message "note: loop does not exit normally" } */
+ while (1);
+ f (); /* { dg-warning "statement expression is unreachable" } */
+}
+
+void unreachable_after_for_loop (void)
+{
+ for (;;) /* { dg-message "note: loop does not exit normally" } */
+ f ();
+ f (); /* { dg-warning "statement expression is unreachable" } */
+}
+
+int unreachable_return_after_for_loop (void)
+{
+ for (;;) /* { dg-message "note: loop does not exit normally" } */
+ f ();
+ return g (); /* { dg-warning "statement is unreachable" } */
+}
+
+void break_nested_in_loop_in_switch (int i)
+{
+ switch (i)
+ {
+ case 1:
+ while (1) {
+ return; /* { dg-message "note:.*return.*redirects control flow" } */
+ break; /* { dg-warning "statement is unreachable" } */
+ }
+ break;
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/Wunreachable-code-2.c b/gcc/testsuite/gcc.dg/Wunreachable-code-2.c
new file mode 100644
index 0000000..408ce50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wunreachable-code-2.c
@@ -0,0 +1,86 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunreachable-code" } */
+
+/* The test cases in this file should not produce any warnings because
+ they demonstrate common coding patterns involving (trivially)
+ unreachable code. */
+
+int f(void);
+int g(void);
+int noreturn(void) __attribute__((noreturn));
+
+void abort(void) __attribute__((noreturn));
+#define assert(expr) do { if (!(expr)) abort (); } while (0)
+
+void abort_after_infinite_loop (void)
+{
+ for (;;)
+ f ();
+ abort ();
+}
+
+void abort_after_if (int flag)
+{
+ if (flag)
+ return f ();
+ else
+ return g ();
+ abort ();
+}
+
+void assert_after_infinite_loop (void)
+{
+ for (;;)
+ f ();
+ assert(0 && "unreachable");
+}
+
+void assert_after_if (int flag)
+{
+ if (flag)
+ return f ();
+ else
+ return g ();
+ assert(0 && "unreachable");
+}
+
+int return_after_infinite_loop (void)
+{
+ for (;;)
+ f ();
+ return 0;
+}
+
+int abort_return_after_infinite_loop (void)
+{
+ for (;;)
+ f ();
+ abort ();
+ return 0;
+}
+
+int noreturn_in_return (void)
+{
+ f ();
+ return noreturn ();
+}
+
+int abort_after_noreturn (void)
+{
+ noreturn ();
+ abort ();
+}
+
+int abort_in_do (void)
+{
+ do {
+ abort (0);
+ } while (0);
+}
+
+int assert_in_do (void)
+{
+ do {
+ assert(0 && "abort");
+ } while (0);
+}
diff --git a/gcc/testsuite/gcc.dg/Wunreachable-code-3.c b/gcc/testsuite/gcc.dg/Wunreachable-code-3.c
new file mode 100644
index 0000000..ef0c6bf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wunreachable-code-3.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunreachable-code" } */
+
+/* These tests should produce unreachable warnings. */
+
+int f(void);
+int g(int);
+int noreturn(void) __attribute__((noreturn));
+
+void abort(void) __attribute__((noreturn));
+#define assert(expr) do { if (!(expr)) abort (); } while (0)
+
+void noreturn_unreachable (void)
+{
+ abort ();
+ f (); /* { dg-warning "unreachable" } */
+}
+
+void noreturn_expression (int flag)
+{
+ int x;
+ if (flag)
+ g (noreturn ()); /* { dg-warning "unreachable" } */
+ if (flag)
+ x = noreturn (); /* { dg-warning "unreachable" } */
+}
+
+int noreturn_if_condition (void)
+{
+ if (noreturn ()) /* { dg-warning "unreachable" } */
+ return f ();
+ else
+ return g(1);
+}
+
+void noreturn_while (void)
+{
+ while (noreturn ()) /* { dg-warning "unreachable" } */
+ f ();
+}
+
+void noreturn_do_while (void)
+{
+ do
+ f ();
+ while (noreturn ()); /* { dg-warning "unreachable" } */
+}
diff --git a/libgomp/config/linux/affinity.c b/libgomp/config/linux/affinity.c
index 2f8a300..cbfd281 100644
--- a/libgomp/config/linux/affinity.c
+++ b/libgomp/config/linux/affinity.c
@@ -320,7 +320,6 @@ gomp_affinity_init_level (int level, unsigned long count, bool quiet)
}
return true;
}
- return false;
}
void