Hi!

A nested switch that can fallthrough (either because it has break stmts
or because it doesn't have default: and doesn't cover all cases) isn't
unfortunately reported with -Wimplicit-fallthrough, because we first
gimplify the nested switch and then we just see a label added implicitly
for break; or for the default: case if it was missing and the cases don't
cover all possible values and punt on that.

The following patch fixes it by telling the -Wimplicit-fallthrough
code about the special labels added for break; or missing default:
through a new SWITCH_BREAK_LABEL_P flag.  Additionally it makes sure
that GIMPLE_LABEL is emitted still inside of the switch's body sequence
and if it is a nested switch, it wraps the GIMPLE_SWITCH with the body
that ends in a GIMPLE_LABEL with SWITCH_BREAK_LABEL_P in a GIMPLE_BIND
(without vars or blocks).  With this, the -Wimplicit-fallthrough code
can recognize this and treat the whole switch that can fall through as
a single statement for the purpose of the warning.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2017-11-30  Jakub Jelinek  <ja...@redhat.com>

        PR c/79153
        * tree.h (SWITCH_BREAK_LABEL_P): Define.
        * gimplify.c (collect_fallthrough_labels): Handle GIMPLE_BIND
        starting with a GIMPLE_SWITCH and ending with GIMPLE_LABEL with
        SWITCH_BREAK_LABEL_P set on the label.
        (gimplify_switch_expr): Set SWITCH_BREAK_LABEL_P on the label
        added for default case if it was missing and not all cases covered.
        Wrap GIMPLE_SWITCH and the switch_body_seq into a GIMPLE_BIND if
        switch_body_seq ends with a GIMPLE_LABEL with SWITCH_BREAK_LABEL_P
        set on the label.
        * tree-chrec.c (evolution_function_is_univariate_p): Add return true;
        to avoid -Wimplicit-fallthrough warning.
        * config/i386/i386.c (ix86_expand_special_args_builtin): Add
        FALLTHRU comment to avoid -Wimplicit-fallthrough warning.
c/
        * c-parser.c: Include tree-iterator.h.
        (c_parser_switch_statement): Emit LABEL_EXPR for the break label
        into SWITCH_BODY instead of after it and set SWITCH_BREAK_LABEL_P
        on it.
cp/
        * cp-gimplify.c (genericize_switch_stmt): Emit LABEL_EXPR for the
        break label into SWITCH_BODY instead of after it and set
        SWITCH_BREAK_LABEL_P on it.
        * parser.c (cp_parser_objc_expression): Add FALLTHRU comment to avoid
        -Wimplicit-fallthrough warning.
fortran/
        * match.c (gfc_match): Add FALLTHRU comment to avoid
        -Wimplicit-fallthrough warning.
testsuite/
        * c-c++-common/Wimplicit-fallthrough-7.c: Adjust expected warning
        line.
        * c-c++-common/Wimplicit-fallthrough-36.c: New test.

--- gcc/tree.h.jj       2017-11-28 22:21:17.000000000 +0100
+++ gcc/tree.h  2017-11-30 17:55:35.299197618 +0100
@@ -766,6 +766,11 @@ extern void omp_clause_range_check_faile
 #define FALLTHROUGH_LABEL_P(NODE) \
   (LABEL_DECL_CHECK (NODE)->base.private_flag)
 
+/* Set on the artificial label created for break; stmt from a switch.
+   This is used to implement -Wimplicit-fallthrough.  */
+#define SWITCH_BREAK_LABEL_P(NODE) \
+  (LABEL_DECL_CHECK (NODE)->base.protected_flag)
+
 /* Nonzero means this expression is volatile in the C sense:
    its address should be of type `volatile WHATEVER *'.
    In other words, the declared item is volatile qualified.
--- gcc/gimplify.c.jj   2017-11-28 12:11:40.000000000 +0100
+++ gcc/gimplify.c      2017-11-30 19:04:32.694173903 +0100
@@ -1897,6 +1897,27 @@ collect_fallthrough_labels (gimple_stmt_
 
   do
     {
+      if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND)
+       {
+         /* Recognize the special GIMPLE_BIND added by gimplify_switch_expr,
+            which starts on a GIMPLE_SWITCH and ends with a break label.
+            Handle that as a single statement that can fall through.  */
+         gbind *bind = as_a <gbind *> (gsi_stmt (*gsi_p));
+         gimple *first = gimple_seq_first_stmt (gimple_bind_body (bind));
+         gimple *last = gimple_seq_last_stmt (gimple_bind_body (bind));
+         if (last
+             && gimple_code (first) == GIMPLE_SWITCH
+             && gimple_code (last) == GIMPLE_LABEL)
+           {
+             tree label = gimple_label_label (as_a <glabel *> (last));
+             if (SWITCH_BREAK_LABEL_P (label))
+               {
+                 prev = bind;
+                 gsi_next (gsi_p);
+                 continue;
+               }
+           }
+       }
       if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND
          || gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY)
        {
@@ -2315,6 +2336,7 @@ gimplify_switch_expr (tree *expr_p, gimp
       preprocess_case_label_vec_for_gimple (labels, index_type,
                                            &default_case);
 
+      bool add_bind = false;
       if (!default_case)
        {
          glabel *new_default;
@@ -2322,14 +2344,46 @@ gimplify_switch_expr (tree *expr_p, gimp
          default_case
            = build_case_label (NULL_TREE, NULL_TREE,
                                create_artificial_label (UNKNOWN_LOCATION));
+         if (old_in_switch_expr)
+           {
+             SWITCH_BREAK_LABEL_P (CASE_LABEL (default_case)) = 1;
+             add_bind = true;
+           }
          new_default = gimple_build_label (CASE_LABEL (default_case));
          gimplify_seq_add_stmt (&switch_body_seq, new_default);
        }
+      else if (old_in_switch_expr)
+       {
+         gimple *last = gimple_seq_last_stmt (switch_body_seq);
+         if (last && gimple_code (last) == GIMPLE_LABEL)
+           {
+             tree label = gimple_label_label (as_a <glabel *> (last));
+             if (SWITCH_BREAK_LABEL_P (label))
+               add_bind = true;
+           }
+       }
 
       switch_stmt = gimple_build_switch (SWITCH_COND (switch_expr),
-                                          default_case, labels);
-      gimplify_seq_add_stmt (pre_p, switch_stmt);
-      gimplify_seq_add_seq (pre_p, switch_body_seq);
+                                        default_case, labels);
+      /* For the benefit of -Wimplicit-fallthrough, if switch_body_seq
+        ends with a GIMPLE_LABEL holding SWITCH_BREAK_LABEL_P LABEL_DECL,
+        wrap the GIMPLE_SWITCH up to that GIMPLE_LABEL into a GIMPLE_BIND,
+        so that we can easily find the start and end of the switch
+        statement.  */
+      if (add_bind)
+       {
+         gimple_seq bind_body = NULL;
+         gimplify_seq_add_stmt (&bind_body, switch_stmt);
+         gimple_seq_add_seq (&bind_body, switch_body_seq);
+         gbind *bind = gimple_build_bind (NULL_TREE, bind_body, NULL_TREE);
+         gimple_set_location (bind, EXPR_LOCATION (switch_expr));
+         gimplify_seq_add_stmt (pre_p, bind);
+       }
+      else
+       {
+         gimplify_seq_add_stmt (pre_p, switch_stmt);
+         gimplify_seq_add_seq (pre_p, switch_body_seq);
+       }
       labels.release ();
     }
   else
--- gcc/tree-chrec.c.jj 2017-10-11 22:37:51.000000000 +0200
+++ gcc/tree-chrec.c    2017-11-30 20:39:01.613902149 +0100
@@ -1161,6 +1161,7 @@ evolution_function_is_univariate_p (cons
            return false;
          break;
        }
+      return true;
 
     default:
       return true;
--- gcc/config/i386/i386.c.jj   2017-11-30 09:42:46.000000000 +0100
+++ gcc/config/i386/i386.c      2017-11-30 20:40:02.484133842 +0100
@@ -34985,6 +34985,7 @@ ix86_expand_special_args_builtin (const
        default:
          break;
        }
+      /* FALLTHRU */
     case V64QI_FTYPE_PCCHAR_V64QI_UDI:
     case V32QI_FTYPE_PCCHAR_V32QI_USI:
     case V16QI_FTYPE_PCCHAR_V16QI_UHI:
--- gcc/c/c-parser.c.jj 2017-11-30 12:13:49.000000000 +0100
+++ gcc/c/c-parser.c    2017-11-30 18:35:03.735146924 +0100
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.
 #include "run-rtl-passes.h"
 #include "intl.h"
 #include "c-family/name-hint.h"
+#include "tree-iterator.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -5846,14 +5847,15 @@ c_parser_switch_statement (c_parser *par
   if (!open_brace_p && c_parser_peek_token (parser)->type != CPP_SEMICOLON)
     warn_for_multistatement_macros (loc_after_labels, next_loc, switch_loc,
                                    RID_SWITCH);
-  c_finish_case (body, ce.original_type);
   if (c_break_label)
     {
       location_t here = c_parser_peek_token (parser)->location;
       tree t = build1 (LABEL_EXPR, void_type_node, c_break_label);
       SET_EXPR_LOCATION (t, here);
-      add_stmt (t);
+      SWITCH_BREAK_LABEL_P (c_break_label) = 1;
+      append_to_statement_list_force (t, &body);
     }
+  c_finish_case (body, ce.original_type);
   c_break_label = save_break;
   add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99));
   c_parser_maybe_reclassify_token (parser);
--- gcc/cp/cp-gimplify.c.jj     2017-11-28 22:23:34.000000000 +0100
+++ gcc/cp/cp-gimplify.c        2017-11-30 19:26:36.432776337 +0100
@@ -330,11 +330,13 @@ genericize_switch_stmt (tree *stmt_p, in
   cp_walk_tree (&type, cp_genericize_r, data, NULL);
   *walk_subtrees = 0;
 
+  if (TREE_USED (break_block))
+    SWITCH_BREAK_LABEL_P (break_block) = 1;
+  finish_bc_block (&body, bc_break, break_block);
   *stmt_p = build2_loc (stmt_locus, SWITCH_EXPR, type, cond, body);
   SWITCH_ALL_CASES_P (*stmt_p) = SWITCH_STMT_ALL_CASES_P (stmt);
   gcc_checking_assert (!SWITCH_STMT_NO_BREAK_P (stmt)
                       || !TREE_USED (break_block));
-  finish_bc_block (stmt_p, bc_break, break_block);
 }
 
 /* Genericize a CONTINUE_STMT node *STMT_P.  */
--- gcc/cp/parser.c.jj  2017-11-30 09:42:44.000000000 +0100
+++ gcc/cp/parser.c     2017-11-30 20:40:37.868687216 +0100
@@ -29003,6 +29003,7 @@ cp_parser_objc_expression (cp_parser* pa
        default:
          break;
        }
+      /* FALLTHRU */
     default:
       error_at (kwd->location,
                "misplaced %<@%D%> Objective-C++ construct",
--- gcc/fortran/match.c.jj      2017-10-28 09:00:47.000000000 +0200
+++ gcc/fortran/match.c 2017-11-30 20:41:45.464834013 +0100
@@ -1240,6 +1240,7 @@ loop:
        default:
          gfc_internal_error ("gfc_match(): Bad match code %c", c);
        }
+      /* FALLTHRU */
 
     default:
 
--- gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c.jj     2017-04-28 
22:17:03.000000000 +0200
+++ gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c        2017-11-30 
19:14:47.535557117 +0100
@@ -51,9 +51,9 @@ f (int i)
     {
     case 1:
       {
-       switch (i + 2)
+       switch (i + 2) /* { dg-warning "statement may fall through" } */
          case 4:
-           bar (1); /* { dg-warning "statement may fall through" } */
+           bar (1);
          case 5:
            bar (5);
            return;
--- gcc/testsuite/c-c++-common/Wimplicit-fallthrough-36.c.jj    2017-11-30 
19:11:32.029979082 +0100
+++ gcc/testsuite/c-c++-common/Wimplicit-fallthrough-36.c       2017-11-30 
19:11:07.000000000 +0100
@@ -0,0 +1,72 @@
+/* PR c/79153 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+int
+test (int v1, int v2)
+{
+  switch (v1)
+    {
+    case 3:
+      switch (v2)      /* { dg-warning "this statement may fall through" } */
+       {
+       case 1:
+         return 28;
+       case 2:
+         return 38;
+       case 3:
+         return 88;
+       default:
+         break;
+       }
+    case 4:            /* { dg-message "here" } */
+      return 168;
+    case 5:
+      switch (v2)      /* { dg-warning "this statement may fall through" } */
+       {
+       case 4:
+         break;
+       case 5:
+         return 38;
+       case 6:
+         return 88;
+       }
+    case 6:            /* { dg-message "here" } */
+      return 169;
+    case 7:
+      switch (v2)      /* { dg-warning "this statement may fall through" } */
+       {
+       case 7:
+         return 38;
+       case 8:
+         return 88;
+       }
+    case 8:            /* { dg-message "here" } */
+      return 170;
+    case 9:
+      switch (v2)      /* { dg-bogus "this statement may fall through" } */
+       {
+       case 9:
+         return 38;
+       case 10:
+         return 88;
+       default:
+         return 89;
+       }
+    case 10:
+      return 171;
+    case 11:
+      switch (v2)      /* { dg-bogus "this statement may fall through" } */
+       {
+       case -__INT_MAX__ - 1 ... 31:
+         return 38;
+       case 32:
+         return 88;
+       case 33 ... __INT_MAX__:
+         return 89;
+       }
+    case 12:
+      return 172;
+    }
+  return -1;
+}

        Jakub

Reply via email to