From: Marek Polacek Date: Mon, 29 Jun 2015 13:12:44 +0000 (+0000) Subject: re PR c/66322 (Linus Torvalds: -Wswitch-bool produces dubious warnings, fails to... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b155cfd9288996f3a1044fb2463c7ac7e757a0df;p=gcc.git re PR c/66322 (Linus Torvalds: -Wswitch-bool produces dubious warnings, fails to notice really bad things) PR c/66322 * c-common.c (check_case_bounds): Add bool * parameter. Set OUTSIDE_RANGE_P. (c_add_case_label): Add bool * parameter. Pass it down to check_case_bounds. (c_do_switch_warnings): Add bool parameters. Implement -Wswitch-bool warning here. * c-common.h (c_add_case_label, c_do_switch_warnings): Update declarations. * c-typeck.c (struct c_switch): Add BOOL_COND_P and OUTSIDE_RANGE_P. (c_start_case): Set BOOL_COND_P and OUTSIDE_RANGE_P. Don't warn about -Wswitch-bool here. (do_case): Update c_add_case_label call. (c_finish_case): Update c_do_switch_warnings call. * decl.c (struct cp_switch): Add OUTSIDE_RANGE_P. (push_switch): Set OUTSIDE_RANGE_P. (pop_switch): Update c_do_switch_warnings call. (finish_case_label): Update c_add_case_label call. * semantics.c (finish_switch_cond): Don't warn about -Wswitch-bool here. * function.c (stack_protect_epilogue): Remove a cast to int. * doc/invoke.texi: Update -Wswitch-bool description. * c-c++-common/pr60439.c: Add dg-prune-output and add switch cases. * c-c++-common/pr66322.c: New test. * g++.dg/eh/scope1.C: Remove dg-warning. From-SVN: r225116 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 976e6894fa2..c33eff9c29a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2015-06-29 Marek Polacek + + PR c/66322 + * function.c (stack_protect_epilogue): Remove a cast to int. + * doc/invoke.texi: Update -Wswitch-bool description. + 2015-06-29 Richard Biener * genmatch.c (add_operator): Treat ADDR_EXPR as atom. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index ffad035ffef..2d6e3c5ec2a 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,15 @@ +2015-06-29 Marek Polacek + + PR c/66322 + * c-common.c (check_case_bounds): Add bool * parameter. Set + OUTSIDE_RANGE_P. + (c_add_case_label): Add bool * parameter. Pass it down to + check_case_bounds. + (c_do_switch_warnings): Add bool parameters. Implement -Wswitch-bool + warning here. + * c-common.h (c_add_case_label, c_do_switch_warnings): Update + declarations. + 2015-06-27 Marek Polacek * c-common.c (check_main_parameter_types): Use VECTOR_TYPE_P diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 06d2abcd451..8156d6a1e2a 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -306,7 +306,8 @@ struct visibility_flags visibility_options; static tree c_fully_fold_internal (tree expr, bool, bool *, bool *, bool); static tree check_case_value (location_t, tree); -static bool check_case_bounds (location_t, tree, tree, tree *, tree *); +static bool check_case_bounds (location_t, tree, tree, tree *, tree *, + bool *); static tree handle_packed_attribute (tree *, tree, tree, int, bool *); static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *); @@ -3633,13 +3634,15 @@ check_case_value (location_t loc, tree value) bound of the case label, and CASE_HIGH_P is the upper bound or NULL if the case is not a case range. The caller has to make sure that we are not called with NULL for - CASE_LOW_P (i.e. the default case). + CASE_LOW_P (i.e. the default case). OUTSIDE_RANGE_P says whether there + was a case value that doesn't fit into the range of the ORIG_TYPE. Returns true if the case label is in range of ORIG_TYPE (saturated or untouched) or false if the label is out of range. */ static bool check_case_bounds (location_t loc, tree type, tree orig_type, - tree *case_low_p, tree *case_high_p) + tree *case_low_p, tree *case_high_p, + bool *outside_range_p) { tree min_value, max_value; tree case_low = *case_low_p; @@ -3658,6 +3661,7 @@ check_case_bounds (location_t loc, tree type, tree orig_type, { warning_at (loc, 0, "case label value is less than minimum value " "for type"); + *outside_range_p = true; return false; } @@ -3666,6 +3670,7 @@ check_case_bounds (location_t loc, tree type, tree orig_type, && tree_int_cst_compare (case_high, max_value) > 0) { warning_at (loc, 0, "case label value exceeds maximum value for type"); + *outside_range_p = true; return false; } @@ -3675,6 +3680,7 @@ check_case_bounds (location_t loc, tree type, tree orig_type, { warning_at (loc, 0, "lower value in case label range" " less than minimum value for type"); + *outside_range_p = true; case_low = min_value; } @@ -3684,6 +3690,7 @@ check_case_bounds (location_t loc, tree type, tree orig_type, { warning_at (loc, 0, "upper value in case label range" " exceeds maximum value for type"); + *outside_range_p = true; case_high = max_value; } @@ -6391,13 +6398,14 @@ case_compare (splay_tree_key k1, splay_tree_key k2) HIGH_VALUE is NULL_TREE, then case label was declared using the usual C/C++ syntax, rather than the GNU case range extension. CASES is a tree containing all the case ranges processed so far; - COND is the condition for the switch-statement itself. Returns the - CASE_LABEL_EXPR created, or ERROR_MARK_NODE if no CASE_LABEL_EXPR - is created. */ + COND is the condition for the switch-statement itself. + OUTSIDE_RANGE_P says whether there was a case value that doesn't + fit into the range of the ORIG_TYPE. Returns the CASE_LABEL_EXPR + created, or ERROR_MARK_NODE if no CASE_LABEL_EXPR is created. */ tree c_add_case_label (location_t loc, splay_tree cases, tree cond, tree orig_type, - tree low_value, tree high_value) + tree low_value, tree high_value, bool *outside_range_p) { tree type; tree label; @@ -6458,7 +6466,8 @@ c_add_case_label (location_t loc, splay_tree cases, tree cond, tree orig_type, don't insert the case label and return NULL_TREE. */ if (low_value && !check_case_bounds (loc, type, orig_type, - &low_value, high_value ? &high_value : NULL)) + &low_value, high_value ? &high_value : NULL, + outside_range_p)) return NULL_TREE; /* Look up the LOW_VALUE in the table of case labels we already @@ -6619,13 +6628,15 @@ match_case_to_enum (splay_tree_node node, void *data) void c_do_switch_warnings (splay_tree cases, location_t switch_location, - tree type, tree cond) + tree type, tree cond, bool bool_cond_p, + bool outside_range_p) { splay_tree_node default_node; splay_tree_node node; tree chain; - if (!warn_switch && !warn_switch_enum && !warn_switch_default) + if (!warn_switch && !warn_switch_enum && !warn_switch_default + && !warn_switch_bool) return; default_node = splay_tree_lookup (cases, (splay_tree_key) NULL); @@ -6633,6 +6644,52 @@ c_do_switch_warnings (splay_tree cases, location_t switch_location, warning_at (switch_location, OPT_Wswitch_default, "switch missing default case"); + /* There are certain cases where -Wswitch-bool warnings aren't + desirable, such as + switch (boolean) + { + case true: ... + case false: ... + } + so be careful here. */ + if (warn_switch_bool && bool_cond_p) + { + splay_tree_node min_node; + /* If there's a default node, it's also the value with the minimal + key. So look at the penultimate key (if any). */ + if (default_node) + min_node = splay_tree_successor (cases, (splay_tree_key) NULL); + else + min_node = splay_tree_min (cases); + tree min = min_node ? (tree) min_node->key : NULL_TREE; + + splay_tree_node max_node = splay_tree_max (cases); + /* This might be a case range, so look at the value with the + maximal key and then check CASE_HIGH. */ + tree max = max_node ? (tree) max_node->value : NULL_TREE; + if (max) + max = CASE_HIGH (max) ? CASE_HIGH (max) : CASE_LOW (max); + + /* If there's a case value > 1 or < 0, that is outside bool + range, warn. */ + if (outside_range_p + || (max && wi::gts_p (max, 1)) + || (min && wi::lts_p (min, 0)) + /* And handle the + switch (boolean) + { + case true: ... + case false: ... + default: ... + } + case, where we want to warn. */ + || (default_node + && max && wi::eq_p (max, 1) + && min && wi::eq_p (min, 0))) + warning_at (switch_location, OPT_Wswitch_bool, + "switch condition has boolean value"); + } + /* From here on, we only care about about enumerated types. */ if (!type || TREE_CODE (type) != ENUMERAL_TYPE) return; diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 8951c3f9fb6..2b03703af86 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -946,9 +946,11 @@ extern tree boolean_increment (enum tree_code, tree); extern int case_compare (splay_tree_key, splay_tree_key); -extern tree c_add_case_label (location_t, splay_tree, tree, tree, tree, tree); +extern tree c_add_case_label (location_t, splay_tree, tree, tree, tree, tree, + bool *); -extern void c_do_switch_warnings (splay_tree, location_t, tree, tree); +extern void c_do_switch_warnings (splay_tree, location_t, tree, tree, bool, + bool); extern tree build_function_call (location_t, tree, tree); diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index edba3f1f51b..2c5ba6db6a4 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,12 @@ +2015-06-29 Marek Polacek + + PR c/66322 + * c-typeck.c (struct c_switch): Add BOOL_COND_P and OUTSIDE_RANGE_P. + (c_start_case): Set BOOL_COND_P and OUTSIDE_RANGE_P. Don't warn + about -Wswitch-bool here. + (do_case): Update c_add_case_label call. + (c_finish_case): Update c_do_switch_warnings call. + 2015-06-27 Marek Polacek * c-typeck.c: Use VECTOR_TYPE_P throughout. diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 8e2696a7a21..6ea35137d50 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -9525,6 +9525,14 @@ struct c_switch { /* The next node on the stack. */ struct c_switch *next; + + /* Remember whether the controlling expression had boolean type + before integer promotions for the sake of -Wswitch-bool. */ + bool bool_cond_p; + + /* Remember whether there was a case value that is outside the + range of the ORIG_TYPE. */ + bool outside_range_p; }; /* A stack of the currently active switch statements. The innermost @@ -9538,7 +9546,7 @@ struct c_switch *c_switch_stack; /* Start a C switch statement, testing expression EXP. Return the new SWITCH_EXPR. SWITCH_LOC is the location of the `switch'. SWITCH_COND_LOC is the location of the switch's condition. - EXPLICIT_CAST_P is true if the expression EXP has explicit cast. */ + EXPLICIT_CAST_P is true if the expression EXP has an explicit cast. */ tree c_start_case (location_t switch_loc, @@ -9546,6 +9554,7 @@ c_start_case (location_t switch_loc, tree exp, bool explicit_cast_p) { tree orig_type = error_mark_node; + bool bool_cond_p = false; struct c_switch *cs; if (exp != error_mark_node) @@ -9575,8 +9584,7 @@ c_start_case (location_t switch_loc, /* Explicit cast to int suppresses this warning. */ && !(TREE_CODE (type) == INTEGER_TYPE && explicit_cast_p)) - warning_at (switch_cond_loc, OPT_Wswitch_bool, - "switch condition has boolean value"); + bool_cond_p = true; if (!in_system_header_at (input_location) && (type == long_integer_type_node @@ -9600,6 +9608,8 @@ c_start_case (location_t switch_loc, cs->orig_type = orig_type; cs->cases = splay_tree_new (case_compare, NULL, NULL); cs->bindings = c_get_switch_bindings (); + cs->bool_cond_p = bool_cond_p; + cs->outside_range_p = false; cs->next = c_switch_stack; c_switch_stack = cs; @@ -9646,7 +9656,8 @@ do_case (location_t loc, tree low_value, tree high_value) label = c_add_case_label (loc, c_switch_stack->cases, SWITCH_COND (c_switch_stack->switch_expr), c_switch_stack->orig_type, - low_value, high_value); + low_value, high_value, + &c_switch_stack->outside_range_p); if (label == error_mark_node) label = NULL_TREE; return label; @@ -9667,7 +9678,8 @@ c_finish_case (tree body, tree type) switch_location = EXPR_LOCATION (cs->switch_expr); c_do_switch_warnings (cs->cases, switch_location, type ? type : TREE_TYPE (cs->switch_expr), - SWITCH_COND (cs->switch_expr)); + SWITCH_COND (cs->switch_expr), + cs->bool_cond_p, cs->outside_range_p); /* Pop the stack. */ c_switch_stack = cs->next; diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4a3b2d8a071..c16b068f8f2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2015-06-29 Marek Polacek + + PR c/66322 + * decl.c (struct cp_switch): Add OUTSIDE_RANGE_P. + (push_switch): Set OUTSIDE_RANGE_P. + (pop_switch): Update c_do_switch_warnings call. + (finish_case_label): Update c_add_case_label call. + * semantics.c (finish_switch_cond): Don't warn about -Wswitch-bool + here. + 2015-06-27 Marek Polacek * call.c: Use VECTOR_TYPE_P. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 0683f26b7a0..498ed71bcf9 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -3208,6 +3208,9 @@ struct cp_switch label. We need a tree, rather than simply a hash table, because of the GNU case range extension. */ splay_tree cases; + /* Remember whether there was a case value that is outside the + range of the original type of the controlling expression. */ + bool outside_range_p; }; /* A stack of the currently active switch statements. The innermost @@ -3229,6 +3232,7 @@ push_switch (tree switch_stmt) p->next = switch_stack; p->switch_stmt = switch_stmt; p->cases = splay_tree_new (case_compare, NULL, NULL); + p->outside_range_p = false; switch_stack = p; } @@ -3240,10 +3244,14 @@ pop_switch (void) /* Emit warnings as needed. */ switch_location = EXPR_LOC_OR_LOC (cs->switch_stmt, input_location); + const bool bool_cond_p + = (SWITCH_STMT_TYPE (cs->switch_stmt) + && TREE_CODE (SWITCH_STMT_TYPE (cs->switch_stmt)) == BOOLEAN_TYPE); if (!processing_template_decl) c_do_switch_warnings (cs->cases, switch_location, SWITCH_STMT_TYPE (cs->switch_stmt), - SWITCH_STMT_COND (cs->switch_stmt)); + SWITCH_STMT_COND (cs->switch_stmt), + bool_cond_p, cs->outside_range_p); splay_tree_delete (cs->cases); switch_stack = switch_stack->next; @@ -3308,7 +3316,8 @@ finish_case_label (location_t loc, tree low_value, tree high_value) high_value = case_conversion (type, high_value); r = c_add_case_label (loc, switch_stack->cases, cond, type, - low_value, high_value); + low_value, high_value, + &switch_stack->outside_range_p); /* After labels, make any new cleanups in the function go into their own new (temporary) binding contour. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4b8ce3b35ce..c23d9bef0c2 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -1156,11 +1156,6 @@ finish_switch_cond (tree cond, tree switch_stmt) orig_type = TREE_TYPE (cond); if (cond != error_mark_node) { - /* Warn if the condition has boolean value. */ - if (TREE_CODE (orig_type) == BOOLEAN_TYPE) - warning_at (input_location, OPT_Wswitch_bool, - "switch condition has type bool"); - /* [stmt.switch] Integral promotions are performed. */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 844d7edaecf..a765df5936b 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -4012,7 +4012,8 @@ warning about an omitted enumeration code even if there is a @item -Wswitch-bool @opindex Wswitch-bool @opindex Wno-switch-bool -Warn whenever a @code{switch} statement has an index of boolean type. +Warn whenever a @code{switch} statement has an index of boolean type +and the case values are outside the range of a boolean type. It is possible to suppress this warning by casting the controlling expression to a type other than @code{bool}. For example: @smallexample diff --git a/gcc/function.c b/gcc/function.c index cffe32322d2..4389bbd1aba 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -4891,7 +4891,7 @@ stack_protect_epilogue (void) /* Allow the target to compare Y with X without leaking either into a register. */ - switch ((int) (HAVE_stack_protect_test != 0)) + switch (HAVE_stack_protect_test != 0) { case 1: tmp = gen_stack_protect_test (x, y, label); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b4bd5dae784..fc2b9bc0d76 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2015-06-29 Marek Polacek + + PR c/66322 + * c-c++-common/pr60439.c: Add dg-prune-output and add switch cases. + * c-c++-common/pr66322.c: New test. + * g++.dg/eh/scope1.C: Remove dg-warning. + 2015-06-29 Richard Biener PR tree-optimization/66677 diff --git a/gcc/testsuite/c-c++-common/pr60439.c b/gcc/testsuite/c-c++-common/pr60439.c index 3368a0b944d..68bd33c22cb 100644 --- a/gcc/testsuite/c-c++-common/pr60439.c +++ b/gcc/testsuite/c-c++-common/pr60439.c @@ -1,5 +1,6 @@ /* PR c/60439 */ /* { dg-do compile } */ +/* { dg-prune-output "case label value exceeds" } */ #ifndef __cplusplus # define bool _Bool @@ -11,18 +12,30 @@ void f1 (bool b) { switch (b) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } } void f2 (int a, int b) { switch (a && b) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch ((bool) (a && b)) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch ((a && b) || a) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } /* No warnings on following. */ switch ((int) (a && b)) break; @@ -38,35 +51,65 @@ void f3 (int a) { switch (!!a) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (!a) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } } void f4 (void) { switch (foo ()) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } } void f5 (int a) { switch (a == 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a != 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a > 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a < 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a <= 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a >= 3) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (foo (), foo (), a >= 42) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (a == 3, a & 4, a ^ 5, a) break; switch ((int) (a == 3)) @@ -79,11 +122,20 @@ void f6 (bool b) { switch (b) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (!b) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } switch (b++) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } } void @@ -91,7 +143,10 @@ f7 (void) { bool b; switch (b = 1) /* { dg-warning "switch condition has" } */ - break; + { + case 3: + break; + } } void @@ -104,5 +159,8 @@ f8 (int i) switch ((unsigned int) i) break; switch ((bool) i) /* { dg-warning "switch condition has" } */ - break; + { + case 11: + break; + } } diff --git a/gcc/testsuite/c-c++-common/pr66322.c b/gcc/testsuite/c-c++-common/pr66322.c new file mode 100644 index 00000000000..eb1e9e4a2ed --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr66322.c @@ -0,0 +1,144 @@ +/* PR c/66322 */ +/* { dg-do compile } */ + +#ifndef __cplusplus +# define bool _Bool +# define true 1 +# define false 0 +#endif + +void +nowarn (bool b) +{ + switch (b) + ; + + switch (b) + { + case true: + case false: + break; + } + + switch (b) + { + case true: + break; + } + + switch (b) + { + case true: + default: + break; + } + + switch (b) + { + case false: + break; + } + + switch (b) + { + case false: + default: + break; + } + + switch (b) + { + default: + break; + } + + switch (b) + { + case false ... true: + break; + } + + switch (b) + { + case 1: + switch (b) + { + case true: + default: + break; + } + default: + break; + } +} + +void +warn (bool b) +{ + switch (b) /* { dg-warning "switch condition has" } */ + { + case true: + case false: + default: + break; + } + + switch (b) /* { dg-warning "switch condition has" } */ + { + case false ... true: + default: + break; + } +} + +void +warn2 (int n) +{ + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case 0 ... 2: /* { dg-warning "upper value" "" { target c++ } } */ + default: + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case 1 ... 10: /* { dg-warning "upper value" "" { target c++ } } */ + default: + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case 2: /* { dg-warning "case label" "" { target c++ } } */ + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case 0: + case 1: + case -1: /* { dg-warning "case label" "" { target c++ } } */ + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case -1 ... 1: /* { dg-warning "lower value" "" { target c++ } } */ + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case -1 ... 0: /* { dg-warning "lower value" "" { target c++ } } */ + default: + break; + } + + switch (n == 2) /* { dg-warning "switch condition has" } */ + { + case -10 ... -1: /* { dg-warning "case label" "" { target c++ } } */ + default: + break; + } +} diff --git a/gcc/testsuite/g++.dg/eh/scope1.C b/gcc/testsuite/g++.dg/eh/scope1.C index 8d553d8295b..276e0d6e588 100644 --- a/gcc/testsuite/g++.dg/eh/scope1.C +++ b/gcc/testsuite/g++.dg/eh/scope1.C @@ -31,7 +31,7 @@ void f3 () void f4 () { - switch (C br = C()) /* { dg-warning "switch condition has" } */ + switch (C br = C()) { default: abort ();