Ping. On Wed, Jun 10, 2015 at 08:02:01PM +0200, Marek Polacek wrote: > Linus's kind words here <https://lkml.org/lkml/2015/5/27/941> prodded > me to improving the -Wswitch-bool warning. In particular, this patch > makes the warning not warn when the case values aren't outside bool > range. But it will still warn about > > switch (boolean) > { > case false ... true: ... > default: ... > } > > That of course means that the warning must be delayed to the point where > all the case labels have been parsed. Then there are these annoying > differences between C and C++ (type of a comparison), which means some > splay trees fun. Another problem stems from the fact that case labels > that are outside the range of the original type of the switch controlling > expression are dropped on the floor, so I had to add the OUTSIDE_RANGE_P > flag. Case ranges are fun as well. > > If this patch is approved, I'd like to backport it even to 5 branch after > some time so it's fixed in 5.2. > > There's also the possibility to add -Wswitch-bool=2 that would warn every > time the compiler sees a switch with a boolean controlling expr, i.e. do > what we're doing now. But that would be something for trunk only. > > Thoughts? > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > 2015-06-10 Marek Polacek <pola...@redhat.com> > > 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. > > diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi > index 5903c75..3746de7 100644 > --- gcc/doc/invoke.texi > +++ 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 gcc/c-family/c-common.c gcc/c-family/c-common.c > index 5b76567..6f9f056 100644 > --- gcc/c-family/c-common.c > +++ gcc/c-family/c-common.c > @@ -312,7 +312,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 *); > @@ -3639,13 +3640,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; > @@ -3664,6 +3667,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; > } > > @@ -3672,6 +3676,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; > } > > @@ -3681,6 +3686,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; > } > > @@ -3690,6 +3696,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; > } > > @@ -6383,13 +6390,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; > @@ -6450,7 +6458,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 > @@ -6611,13 +6620,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); > @@ -6625,6 +6636,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 gcc/c-family/c-common.h gcc/c-family/c-common.h > index 7ba81c4..3578929 100644 > --- gcc/c-family/c-common.h > +++ gcc/c-family/c-common.h > @@ -947,9 +947,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 gcc/c/c-typeck.c gcc/c/c-typeck.c > index 636e0bb..04a50c7 100644 > --- gcc/c/c-typeck.c > +++ gcc/c/c-typeck.c > @@ -9529,6 +9529,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 > @@ -9542,7 +9550,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, > @@ -9550,6 +9558,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) > @@ -9579,8 +9588,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 > @@ -9604,6 +9612,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; > > @@ -9650,7 +9660,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; > @@ -9671,7 +9682,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 gcc/cp/decl.c gcc/cp/decl.c > index 3bed538..c7b27ff 100644 > --- gcc/cp/decl.c > +++ gcc/cp/decl.c > @@ -3212,6 +3212,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 > @@ -3233,6 +3236,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; > } > > @@ -3244,10 +3248,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; > @@ -3312,7 +3320,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 gcc/cp/semantics.c gcc/cp/semantics.c > index 59ec9047..aa43022 100644 > --- gcc/cp/semantics.c > +++ gcc/cp/semantics.c > @@ -1161,11 +1161,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 gcc/function.c gcc/function.c > index 8baaed7..0b45abf 100644 > --- gcc/function.c > +++ gcc/function.c > @@ -4934,7 +4934,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 gcc/testsuite/c-c++-common/pr60439.c > gcc/testsuite/c-c++-common/pr60439.c > index 3368a0b..68bd33c 100644 > --- gcc/testsuite/c-c++-common/pr60439.c > +++ 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 gcc/testsuite/c-c++-common/pr66322.c > gcc/testsuite/c-c++-common/pr66322.c > index e69de29..eb1e9e4 100644 > --- gcc/testsuite/c-c++-common/pr66322.c > +++ 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 gcc/testsuite/g++.dg/eh/scope1.C gcc/testsuite/g++.dg/eh/scope1.C > index 8d553d8..276e0d6 100644 > --- gcc/testsuite/g++.dg/eh/scope1.C > +++ 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 (); > > Marek
Marek