diff mbox

Implement -Wswitch-fallthrough: core

Message ID 20160711194623.GJ13963@redhat.com
State New
Headers show

Commit Message

Marek Polacek July 11, 2016, 7:46 p.m. UTC
2016-07-11  Marek Polacek  <polacek@redhat.com>

	PR c/7652
	* builtins.c (expand_builtin): Handle BUILT_IN_FALLTHROUGH.
	* builtins.def: Add BUILT_IN_FALLTHROUGH.
	* common.opt (Wswitch-fallthrough): New option.
	* doc/extend.texi: Document __builtin_fallthrough.
	* doc/invoke.texi: Document -Wswitch-fallthrough.
	* gimple-low.c: Include "diagnostic-core.h" and "gimple-walk.h".
	(purge_builtin_fallthrough_r): New function.
	(purge_builtin_fallthrough): Likewise.
	(lower_stmt): Call purge_builtin_fallthrough.
	* gimplify.c (struct label_entry): New struct.
	(find_label_entry): New function.
	(last_stmt_in_scope): Likewise.
	(warn_switch_fallthrough_r): Likewise.
	(maybe_warn_switch_fallthrough): Likewise.
	(gimplify_switch_expr): Call maybe_warn_switch_fallthrough.
	(gimplify_label_expr): New function.
	(gimplify_case_label_expr): Set location.
	(gimplify_expr): Call gimplify_label_expr.
	* system.h (gcc_fallthrough): New macro.

	* c-c++-common/Wswitch-fallthrough-1.c: New test.
	* c-c++-common/Wswitch-fallthrough-10.c: New test.
	* c-c++-common/Wswitch-fallthrough-11.c: New test.
	* c-c++-common/Wswitch-fallthrough-12.c: New test.
	* c-c++-common/Wswitch-fallthrough-2.c: New test.
	* c-c++-common/Wswitch-fallthrough-3.c: New test.
	* c-c++-common/Wswitch-fallthrough-4.c: New test.
	* c-c++-common/Wswitch-fallthrough-5.c: New test.
	* c-c++-common/Wswitch-fallthrough-6.c: New test.
	* c-c++-common/Wswitch-fallthrough-7.c: New test.
	* c-c++-common/Wswitch-fallthrough-8.c: New test.
	* c-c++-common/Wswitch-fallthrough-9.c: New test.
	* c-c++-common/builtin-fallthrough-1.c: New test.
	* c-c++-common/builtin-fallthrough-2.c: New test.
diff mbox

Patch

diff --git gcc/gcc/builtins.c gcc/gcc/builtins.c
index 1465c60..c462bc5 100644
--- gcc/gcc/builtins.c
+++ gcc/gcc/builtins.c
@@ -6885,6 +6889,11 @@  expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
 	 folding.  */
       break;
 
+    case BUILT_IN_FALLTHROUGH:
+      /* We should have gotten rid of this during gimplification.  */
+      error ("%Kinvalid use of %<__builtin_fallthrough ()%>", exp);
+      return const0_rtx;
+
     default:	/* just do library call, if unknown builtin */
       break;
     }
diff --git gcc/gcc/builtins.def gcc/gcc/builtins.def
index 33a7626..c75f022 100644
--- gcc/gcc/builtins.def
+++ gcc/gcc/builtins.def
@@ -851,6 +851,7 @@  DEF_GCC_BUILTIN        (BUILT_IN_VA_ARG_PACK, "va_arg_pack", BT_FN_INT, ATTR_PUR
 DEF_GCC_BUILTIN        (BUILT_IN_VA_ARG_PACK_LEN, "va_arg_pack_len", BT_FN_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN__EXIT, "_exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN__EXIT2, "_Exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN	       (BUILT_IN_FALLTHROUGH, "fallthrough", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
 
 /* Implementing nested functions.  */
 DEF_BUILTIN_STUB (BUILT_IN_INIT_TRAMPOLINE, "__builtin_init_trampoline")
diff --git gcc/gcc/common.opt gcc/gcc/common.opt
index a7c5125..741da8d 100644
--- gcc/gcc/common.opt
+++ gcc/gcc/common.opt
@@ -707,6 +707,10 @@  Wsuggest-final-methods
 Common Var(warn_suggest_final_methods) Warning
 Warn about C++ virtual methods where adding final keyword would improve code quality.
 
+Wswitch-fallthrough
+Common Var(warn_switch_fallthrough) Warning Init(1)
+Warn when a switch case falls through.
+
 Wswitch-unreachable
 Common Var(warn_switch_unreachable) Warning Init(1)
 Warn about statements between switch's controlling expression and the first
diff --git gcc/gcc/doc/extend.texi gcc/gcc/doc/extend.texi
index 5b9e617..5fc129d 100644
--- gcc/gcc/doc/extend.texi
+++ gcc/gcc/doc/extend.texi
@@ -11127,6 +11127,25 @@  if (__builtin_expect (ptr != NULL, 1))
 when testing pointer or floating-point values.
 @end deftypefn
 
+@deftypefn {Built-in Function} void __builtin_fallthrough (void)
+When the @option{-Wswitch-fallthrough} command-line options is enabled, the
+compiler warns when a switch case statement falls through.  To suppress
+this warning in a case where the fall through is desirable, it is possible
+to use this function as in the below:
+
+@smallexample
+switch (cond)
+  @{
+  case 1:
+    bar (0);
+    /* FALLTHRU */
+    __builtin_fallthrough ();
+  default:
+    @dots{}
+  @}
+@end smallexample
+@end deftypefn
+
 @deftypefn {Built-in Function} void __builtin_trap (void)
 This function causes the program to exit abnormally.  GCC implements
 this function by using a target-dependent mechanism (such as
diff --git gcc/gcc/doc/invoke.texi gcc/gcc/doc/invoke.texi
index ba44951..0244643 100644
--- gcc/gcc/doc/invoke.texi
+++ gcc/gcc/doc/invoke.texi
@@ -300,7 +300,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
 -Wmissing-format-attribute -Wsubobject-linkage @gol
 -Wswitch  -Wswitch-bool  -Wswitch-default  -Wswitch-enum @gol
--Wswitch-unreachable  -Wsync-nand @gol
+-Wswitch-fallthrough  -Wswitch-unreachable  -Wsync-nand @gol
 -Wsystem-headers  -Wtautological-compare  -Wtrampolines  -Wtrigraphs @gol
 -Wtype-limits  -Wundef @gol
 -Wuninitialized  -Wunknown-pragmas  -Wunsafe-loop-optimizations @gol
@@ -4194,6 +4194,69 @@  switch ((int) (a == 4))
 @end smallexample
 This warning is enabled by default for C and C++ programs.
 
+@item -Wswitch-fallthrough
+@opindex Wswitch-fallthrough
+@opindex Wno-switch-fallthrough
+Warn when a switch case falls through.  For example:
+
+@smallexample
+@group
+switch (cond)
+  @{
+  case 1:
+    a = 1;
+    break;
+  case 2:
+    a = 2;
+  case 3:
+    a = 3;
+    break;
+  @}
+@end group
+@end smallexample
+
+This warning does not warn when the last statement of a case cannot
+fall through, e.g. when there is a return statement of a function
+declared with the noreturn attribute.  @option{-Wswitch-fallthrough}
+also takes into account control flow statements, such as ifs, and only
+warns when appropriate.  E.g.@:
+
+@smallexample
+@group
+switch (cond)
+  @{
+  case 1:
+    if (i > 3) @{
+      bar (5);
+      break;
+    @} else if (i < 1) @{
+      bar (0);
+    @} else
+      return;
+  default:
+    @dots{}
+  @}
+@end group
+@end smallexample
+
+Since there are occasions where a switch case fall through is desirable,
+GCC provides a built-in function, @samp{__builtin_fallthrough}, which can
+be used to suppres this warning what would normally occur:
+
+@smallexample
+@group
+switch (cond)
+  @{
+  case 1:
+    bar (0);
+    /* FALLTHRU */
+    __builtin_fallthrough ();
+  default:
+    @dots{}
+  @}
+@end group
+@end smallexample
+
 @item -Wswitch-unreachable
 @opindex Wswitch-unreachable
 @opindex Wno-switch-unreachable
diff --git gcc/gcc/gimple-low.c gcc/gcc/gimple-low.c
index 9ea17af..70c957b 100644
--- gcc/gcc/gimple-low.c
+++ gcc/gcc/gimple-low.c
@@ -30,6 +30,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "calls.h"
 #include "gimple-iterator.h"
 #include "gimple-low.h"
+#include "diagnostic-core.h"
+#include "gimple-walk.h"
 
 /* The differences between High GIMPLE and Low GIMPLE are the
    following:
@@ -220,6 +222,56 @@  lower_omp_directive (gimple_stmt_iterator *gsi, struct lower_data *data)
   gsi_next (gsi);
 }
 
+/* Callback for walk_gimple_seq.  */
+
+static tree
+purge_builtin_fallthrough_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
+			     struct walk_stmt_info *)
+{
+  gimple *stmt = gsi_stmt (*gsi_p);
+
+  *handled_ops_p = true;
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_TRY:
+    case GIMPLE_BIND:
+    case GIMPLE_CATCH:
+    case GIMPLE_EH_FILTER:
+    case GIMPLE_TRANSACTION:
+      /* Walk the sub-statements.  */
+      *handled_ops_p = false;
+      break;
+    case GIMPLE_CALL:
+      if (gimple_call_builtin_p (stmt, BUILT_IN_FALLTHROUGH))
+	{
+	  location_t bfloc = gimple_location (stmt);
+	  gsi_remove (gsi_p, true);
+	  if (gsi_end_p (*gsi_p))
+	    return integer_zero_node;
+	  else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL
+		   || (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+		       && !gimple_has_location (gsi_stmt (*gsi_p))))
+	    /* This usage is OK.  */;
+	  else
+	    warning_at (bfloc, 0, "%<__builtin_fallthrough ()%> not "
+			"preceding a label");
+	}
+      break;
+    default:
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* Remove all __builtin_fallthrough calls in SEQ.  */
+
+static void
+purge_builtin_fallthrough (gimple_seq *seq_p)
+{
+  struct walk_stmt_info wi;
+  memset (&wi, 0, sizeof (wi));
+  walk_gimple_seq_mod (seq_p, purge_builtin_fallthrough_r, NULL, &wi);
+}
 
 /* Lower statement GSI.  DATA is passed through the recursion.  We try to
    track the fallthruness of statements and get rid of unreachable return
@@ -242,9 +294,13 @@  lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data)
       /* Propagate fallthruness.  */
       return;
 
+    case GIMPLE_SWITCH:
+      /* Get rid of __builtin_fallthrough.  If they occur outside a case
+	 sequence, we will complain later.  */
+      purge_builtin_fallthrough (&gsi->ptr);
+      gcc_fallthrough ();
     case GIMPLE_COND:
     case GIMPLE_GOTO:
-    case GIMPLE_SWITCH:
       data->cannot_fallthru = true;
       gsi_next (gsi);
       return;
diff --git gcc/gcc/gimplify.c gcc/gcc/gimplify.c
index 47c4d25..3b01cb4 100644
--- gcc/gcc/gimplify.c
+++ gcc/gcc/gimplify.c
@@ -1626,6 +1627,254 @@  maybe_warn_switch_unreachable (gimple_seq seq)
     }
 }
 
+
+/* A label entry that pairs label and a location.  */
+struct label_entry
+{
+  tree label;
+  location_t loc;
+};
+
+/* Find LABEL in vector of label entries VEC.  */
+
+static struct label_entry *
+find_label_entry (const auto_vec <struct label_entry> *vec, tree label)
+{
+  unsigned int i;
+  struct label_entry *l;
+
+  FOR_EACH_VEC_ELT (*vec, i, l)
+    if (l->label == label)
+      return l;
+  return NULL;
+}
+
+/* Find the last statement in a scope STMT.  */
+
+static gimple *
+last_stmt_in_scope (gimple *stmt)
+{
+  if (!stmt)
+    return NULL;
+
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_BIND:
+      {
+	gbind *bind = as_a <gbind *> (stmt);
+	return last_stmt_in_scope (
+		 gimple_seq_last_stmt (gimple_bind_body (bind)));
+      }
+
+    case GIMPLE_TRY:
+      {
+	gtry *try_stmt = as_a <gtry *> (stmt);
+	return last_stmt_in_scope (
+		 gimple_seq_last_stmt (gimple_try_eval (try_stmt)));
+      }
+
+    default:
+      return stmt;
+    }
+}
+
+/* Callback for walk_gimple_seq.  */
+
+static tree
+warn_switch_fallthrough_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
+			   struct walk_stmt_info *)
+{
+  gimple *stmt = gsi_stmt (*gsi_p);
+
+  *handled_ops_p = true;
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_TRY:
+    case GIMPLE_BIND:
+    case GIMPLE_CATCH:
+    case GIMPLE_EH_FILTER:
+    case GIMPLE_TRANSACTION:
+      /* Walk the sub-statements.  */
+      *handled_ops_p = false;
+      break;
+
+    /* Find a sequence of form:
+
+       GIMPLE_LABEL
+       [...]
+       <may fallthru stmt>
+       GIMPLE_LABEL
+
+       and possibly warn.  */
+    case GIMPLE_LABEL:
+      {
+	/* Found a label.  Skip all immediately following labels.  */
+	while (!gsi_end_p (*gsi_p)
+	       && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+	  gsi_next (gsi_p);
+
+	/* There might be no more statements.  */
+	if (gsi_end_p (*gsi_p))
+	  return integer_zero_node;
+
+	/* Next statements, if any, are non-label.  */
+	gimple *prev = NULL;
+	/* Vector of labels that fall through.  */
+	auto_vec <struct label_entry> labels;
+
+	do
+	  {
+	    if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND
+		|| gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY)
+	      {
+		/* Nested scope.  Only look at the last statement of
+		   the innermost scope.  */
+		location_t bind_loc = gimple_location (gsi_stmt (*gsi_p));
+		gimple *last = last_stmt_in_scope (gsi_stmt (*gsi_p));
+		if (last)
+		  {
+		    prev = last;
+		    /* It might be a label without a location.  Use the
+		       location of the scope then.  */
+		    if (!gimple_has_location (prev))
+		      gimple_set_location (prev, bind_loc);
+		  }
+		gsi_next (gsi_p);
+		continue;
+	      }
+	    /* If's are tricky.  */
+	    if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_COND)
+	      {
+		gcond *cond_stmt = as_a <gcond *> (gsi_stmt (*gsi_p));
+		tree false_lab = gimple_cond_false_label (cond_stmt);
+		location_t if_loc = gimple_location (cond_stmt);
+
+		/* If we have e.g.
+		   if (i > 1) goto <D.2259>; else goto D;
+		   we can't do much with the else-branch.  */
+		if (!DECL_ARTIFICIAL (false_lab))
+		  break;
+
+		/* Go on until the false label, then one step back.  */
+		for (; !gsi_end_p (*gsi_p); gsi_next (gsi_p))
+		  {
+		    stmt = gsi_stmt (*gsi_p);
+		    if (gimple_code (stmt) == GIMPLE_LABEL
+			&& gimple_label_label (as_a <glabel *> (stmt))
+			     == false_lab)
+		      break;
+		  }
+
+		/* Not found?  Oops.  */
+		if (gsi_end_p (*gsi_p))
+		  break;
+
+		{
+		  struct label_entry l = { false_lab, if_loc };
+		  labels.safe_push (l);
+		}
+
+		/* Go to the last statement of the then branch.  */
+		gsi_prev (gsi_p);
+
+		/* if (i != 0) goto <D.1759>; else goto <D.1760>;
+		   <D.1759>:
+		   <stmt>;
+		   goto <D.1761>;
+		   <D.1760>:
+		 */
+		if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+		    && !gimple_has_location (gsi_stmt (*gsi_p)))
+		  {
+		    /* Look at the statement before, it might be
+		       __builtin_fallthrough, in which case don't
+		       warn.  */
+		    gsi_prev (gsi_p);
+		    bool fallthru_before_dest
+		      = gimple_call_builtin_p (gsi_stmt (*gsi_p),
+					       BUILT_IN_FALLTHROUGH);
+		    gsi_next (gsi_p);
+		    tree goto_dest = gimple_goto_dest (gsi_stmt (*gsi_p));
+		    if (!fallthru_before_dest)
+		      {
+			struct label_entry l = { goto_dest, if_loc };
+			labels.safe_push (l);
+		      }
+		  }
+		/* And move back.  */
+		gsi_next (gsi_p);
+	      }
+	    /* Remember the last statement.  Skip labels that are of no
+	       interest to us.  */
+	    if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+	      {
+		tree label
+		  = gimple_label_label (as_a <glabel *> (gsi_stmt (*gsi_p)));
+		if (find_label_entry (&labels, label))
+		  prev = gsi_stmt (*gsi_p);
+	      }
+	    else
+	      prev = gsi_stmt (*gsi_p);
+	    gsi_next (gsi_p);
+	  }
+	while (!gsi_end_p (*gsi_p)
+	       /* Stop if we find a case or a user-defined label.  */
+	       && (gimple_code (gsi_stmt (*gsi_p)) != GIMPLE_LABEL
+		   || !gimple_has_location (gsi_stmt (*gsi_p))));
+
+	/* There might be no more statements.  */
+	if (gsi_end_p (*gsi_p))
+	  return integer_zero_node;
+
+	gimple *next = gsi_stmt (*gsi_p);
+	/* If what follows is a label, then we may have a fallthrough.  */
+	if (gimple_code (next) == GIMPLE_LABEL
+	    && gimple_has_location (next)
+	    && prev != NULL)
+	  {
+	    tree label;
+	    struct label_entry *l;
+	    if (gimple_code (prev) == GIMPLE_LABEL
+		&& (label = gimple_label_label (as_a <glabel *> (prev)))
+		&& (l = find_label_entry (&labels, label))
+		&& warning_at (l->loc, OPT_Wswitch_fallthrough,
+			       "this statement may fall through"))
+	      inform (gimple_location (next), "here");
+	    else if (!gimple_call_builtin_p (prev, BUILT_IN_FALLTHROUGH)
+		     /* Try to be clever and don't warn when the statement
+			can't actually fall through.  */
+		     && gimple_stmt_may_fallthru (prev)
+		     && gimple_has_location (prev)
+		     && warning_at (gimple_location (prev),
+				    OPT_Wswitch_fallthrough,
+				    "this statement may fall through"))
+	      inform (gimple_location (next), "here");
+
+	    /* So that next warn_switch_fallthrough_r will start looking for
+	       a new sequence starting with this label.  */
+	    gsi_prev (gsi_p);
+	  }
+      }
+      break;
+   default:
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* Warn when a switch case falls through.  */
+
+static void
+maybe_warn_switch_fallthrough (gimple_seq seq)
+{
+  if (!warn_switch_fallthrough || lang_GNU_Fortran ())
+    return;
+
+  struct walk_stmt_info wi;
+  memset (&wi, 0, sizeof (wi));
+  walk_gimple_seq (seq, warn_switch_fallthrough_r, NULL, &wi);
+}
+
 
 /* Gimplify a SWITCH_EXPR, and collect the vector of labels it can
    branch to.  */
@@ -1664,6 +1913,7 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
       gimplify_stmt (&SWITCH_BODY (switch_expr), &switch_body_seq);
 
       maybe_warn_switch_unreachable (switch_body_seq);
+      maybe_warn_switch_fallthrough (switch_body_seq);
 
       labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels = saved_labels;
@@ -1694,6 +1944,21 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
   return GS_ALL_DONE;
 }
 
+/* Gimplify the LABEL_EXPR pointed to by EXPR_P.  */
+
+static enum gimplify_status
+gimplify_label_expr (tree *expr_p, gimple_seq *pre_p)
+{
+  gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
+	      == current_function_decl);
+
+  glabel *label_stmt = gimple_build_label (LABEL_EXPR_LABEL (*expr_p));
+  gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p));
+  gimplify_seq_add_stmt (pre_p, label_stmt);
+
+  return GS_ALL_DONE;
+}
+
 /* Gimplify the CASE_LABEL_EXPR pointed to by EXPR_P.  */
 
 static enum gimplify_status
@@ -1711,6 +1976,7 @@  gimplify_case_label_expr (tree *expr_p, gimple_seq *pre_p)
       break;
 
   label_stmt = gimple_build_label (CASE_LABEL (*expr_p));
+  gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p));
   ctxp->case_labels.safe_push (*expr_p);
   gimplify_seq_add_stmt (pre_p, label_stmt);
 
@@ -10668,11 +10954,7 @@  gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case LABEL_EXPR:
-	  ret = GS_ALL_DONE;
-	  gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
-		      == current_function_decl);
-	  gimplify_seq_add_stmt (pre_p,
-			  gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+	  ret = gimplify_label_expr (expr_p, pre_p);
 	  break;
 
 	case CASE_LABEL_EXPR:
diff --git gcc/gcc/system.h gcc/gcc/system.h
index 78a7da6..14e4a1e 100644
--- gcc/gcc/system.h
+++ gcc/gcc/system.h
@@ -746,6 +746,12 @@  extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
 #define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
 #endif
 
+#if GCC_VERSION >= 7000
+# define gcc_fallthrough() __builtin_fallthrough ()
+#else
+# define gcc_fallthrough()
+#endif
+
 #if GCC_VERSION >= 3001
 #define STATIC_CONSTANT_P(X) (__builtin_constant_p (X) && (X))
 #else
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-1.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-1.c
index e69de29..dc77bcc 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-1.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-1.c
@@ -0,0 +1,38 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+/* Test taken from
+   <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>.  */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+  switch (n)
+    {
+    case 22:
+    case 33:
+      f (1);  /* { dg-warning "statement may fall through" } */
+    case 44:
+      f (2);
+      __builtin_fallthrough ();
+    case 55:
+      if (n > 10)
+	{
+	  f (3);
+	  break;
+	}
+      else
+	{
+	  f (4);
+	  __builtin_fallthrough ();
+	}
+    case 66:
+      f (5);
+     __builtin_fallthrough (); /* { dg-warning "not preceding" } */
+      f (6); /* { dg-warning "statement may fall through" } */
+    case 77:
+       f (7);
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-10.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-10.c
index e69de29..47e8894 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-10.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-10.c
@@ -0,0 +1,227 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      else
+	break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 10)
+	{
+	  bar (3);
+	  __builtin_fallthrough ();
+	}
+      else
+	break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (1);
+	}
+      else
+	break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __builtin_fallthrough ();
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __builtin_fallthrough ();
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      else
+	break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __builtin_fallthrough ();
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __builtin_fallthrough ();
+	}
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (1);
+	  bar (2);
+	}
+      else
+	__builtin_fallthrough ();
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)  /* { dg-warning "statement may fall through" } */
+	{
+	  bar (0);
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	}
+      else
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __builtin_fallthrough ();
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (1);
+	}
+      else
+	{
+	  bar (1);
+	  __builtin_fallthrough ();
+	}
+      case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-11.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-11.c
index e69de29..a204e75 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-11.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-11.c
@@ -0,0 +1,23 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough -O2" } */
+
+/* Prevent false positive with optimizations.  */
+
+extern void g (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i > 10)
+	g (0);
+      else
+	goto L;
+      break;
+L:
+    case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-12.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-12.c
index e69de29..7861a37 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-12.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-12.c
@@ -0,0 +1,26 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough -O2" } */
+
+/* Don't let optimizations preclude the warning.  */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i > 1)
+	bar (1);
+      else
+	goto D;
+      break;
+    case 2:
+      bar (2); /* { dg-warning "statement may fall through" } */
+    D:
+    default:
+      bar (33);
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-2.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-2.c
index e69de29..d8cd826 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-2.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-2.c
@@ -0,0 +1,223 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if without else.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (1);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)  /* { dg-warning "statement may fall through" } */
+	goto L1;
+    case 2:
+L1:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)  /* { dg-warning "statement may fall through" } */
+	goto L2;
+L2:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	goto L3;
+      break;
+    case 2:
+L3:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	goto L4;
+      break;
+L4:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)  /* { dg-warning "statement may fall through" } */
+	if (i > 9)
+	  bar (1);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	if (i > 9)
+	  bar (1);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      { int a; }
+      {
+      if (i)  /* { dg-warning "statement may fall through" } */
+	if (i > 9)
+	  bar (1);
+      }
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      bar (2); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      bar (2);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      bar (2); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      bar (2);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      if (i)
+	bar (2);
+      if (i)
+	bar (3);
+      bar (4); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      if (i)
+	bar (2);
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (3);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      if (i)
+	bar (2);
+      if (i)
+	bar (3);
+      bar (4);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      if (i)
+	bar (2);
+      if (i)
+	bar (3);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-3.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-3.c
index e69de29..a4d62d9 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-3.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-3.c
@@ -0,0 +1,543 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if with else.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (1);
+      else
+	bar (2);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+	bar (2);
+      bar (3); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        return;
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        return;
+      else
+	bar (2);
+      bar (3); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+	return;
+      bar (3); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      else
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      else
+	return;
+      bar (3); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	{
+          bar (1);
+          bar (2);
+          bar (3);
+          bar (4);
+	}
+      else
+	{
+          bar (5);
+          bar (6);
+          bar (7);
+          bar (8);
+	}
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+          bar (1);
+          bar (2);
+          bar (3);
+          bar (4);
+	}
+      else
+	{
+          bar (5);
+          bar (6);
+          bar (7);
+          bar (8);
+	}
+      bar (9); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+        {
+	}
+      else
+	bar (2);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (1);
+      else
+	{
+	}
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	{
+	}
+      else
+	{
+	}
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	return;
+      else
+	{
+	}
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	{
+	}
+      else
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L1;
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+    case 2:
+L1:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L2;
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+L2:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (1);
+      else
+        goto L3;
+    case 2:
+L3:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (1);
+      else
+        goto L4;
+L4:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L5;
+      else
+        goto L5;
+L5:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        bar (1);
+      else
+	bar (2);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        bar (1);
+      else
+	bar (2);
+      bar (3);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        return;
+      else
+	bar (2);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        return;
+      else
+	bar (2);
+      bar (3);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        bar (1);
+      else
+	return;
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+	return;
+      bar (3);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      else
+	return;
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      else
+	return;
+      bar (3);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+          bar (1);
+          bar (2);
+          bar (3);
+          bar (4);
+	}
+      else
+	{
+          bar (5);
+          bar (6);
+          bar (7);
+          bar (8);
+	}
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+          bar (1);
+          bar (2);
+          bar (3);
+          bar (4);
+	}
+      else
+	{
+          bar (5);
+          bar (6);
+          bar (7);
+          bar (8);
+	}
+      bar (9);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        {
+	}
+      else
+	bar (2);
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+	{
+	}
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	}
+      else
+	{
+	}
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	return;
+      else
+	{
+	}
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	}
+      else
+	return;
+      break;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L6;
+      else
+	bar (2);
+      break;
+    case 2:
+L6:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L7;
+      else
+	bar (2);
+      break;
+L7:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+        goto L8;
+      break;
+    case 2:
+L8:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	bar (1);
+      else
+        goto L9;
+      break;
+L9:
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+        goto L10;
+      else
+        goto L10;
+      break;
+L10:
+    case 2:
+      __builtin_abort ();
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-4.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-4.c
index e69de29..7229cde 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-4.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-4.c
@@ -0,0 +1,250 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if with more elses.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	bar (2);
+      else if (i > 15)
+	bar (3);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+	bar (1);
+      else if (i > 10)
+	bar (2);
+      else if (i > 15)
+	bar (3);
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 15)
+	bar (3);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 15)
+	bar (3);
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	bar (3);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	bar (3);
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	bar (4);
+      else if (i > 15)
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	bar (4);
+      else if (i > 15)
+	return;
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10)
+	return;
+      else if (i > 15) /* { dg-warning "statement may fall through" } */
+	bar (3);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10)
+	return;
+      else if (i > 15) /* { dg-warning "statement may fall through" } */
+	bar (3);
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 15)
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+        return;
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 15)
+	return;
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5) /* { dg-warning "statement may fall through" } */
+        bar (1);
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	return;
+      else
+	bar (4);
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+       return;
+      else if (i > 10)
+	return;
+      else if (i > 15) /* { dg-warning "statement may fall through" } */
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+       return;
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	return;
+      else
+	bar (4); /* { dg-warning "statement may fall through" } */
+    case 2:
+      __builtin_abort ();
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 5)
+       return;
+      else if (i > 10)
+	return;
+      else if (i > 15)
+	return;
+      else
+	return;
+    case 2:
+      __builtin_abort ();
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-5.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-5.c
index e69de29..d11a413 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-5.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-5.c
@@ -0,0 +1,109 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+extern void die (void) __attribute__((noreturn));
+
+/* Test may_fallthru-ness.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      bar (0);
+      __builtin_fallthrough ();
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (0);
+      return;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (0);
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (0);
+      goto L1;
+L1:
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (0);
+      die ();
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int i, j, k;
+	bar (0);
+	__builtin_fallthrough ();
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int i, j, k;
+        bar (0);
+        return;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int i, j, k;
+        bar (0);
+        break;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int i, j, k;
+        bar (0);
+        goto L2;
+      }
+L2:
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int i, j, k;
+        bar (0);
+        die ();
+      }
+    case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-6.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-6.c
index e69de29..2e16218 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-6.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-6.c
@@ -0,0 +1,284 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+
+/* Test nested scopes.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	int j;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 10; /* { dg-warning "statement may fall through" } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int k = 9;
+	k++;
+	{
+	  int j = 10;
+	  j++; /* { dg-warning "statement may fall through" } */
+	}
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int k = 9;
+	k++;
+	{
+	  int j = 10;
+	  j++;
+	  {
+	    bar (1); /* { dg-warning "statement may fall through" } */
+	  }
+	}
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	__builtin_fallthrough ();
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	{
+	  int k = j + 5;
+	  bar (k);
+	  __builtin_fallthrough ();
+	}
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	return;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	goto L1;
+      }
+L1:
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 112 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return; /* { dg-warning "statement may fall through" "" { target c++ } 116 } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  return;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 138 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  return; /* { dg-warning "statement may fall through" "" { target c++ } 144 } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  bar (2); /* { dg-warning "statement may fall through" } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 166 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2); /* { dg-warning "statement may fall through" "" { target c++ } 172 } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+      }
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  return;
+      }
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  return;
+      }
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  bar (2);
+      }
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2);
+      }
+      break;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 9;
+	while (1);
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 263 } */
+	int j = 9;
+	switch (j); /* { dg-warning "statement may fall through" "" { target c++ } 265 } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2);
+	__builtin_fallthrough ();
+      }
+    case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-7.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-7.c
index e69de29..f864669 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-7.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-7.c
@@ -0,0 +1,114 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void bar (int);
+extern int bar2 (void);
+extern int *map;
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      bar (0); /* { dg-warning "statement may fall through" } */
+      static int i = 10;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int a[i]; /* { dg-warning "statement may fall through" } */
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      for (int j = 0; j < 10; j++) /* { dg-warning "statement may fall through" "" { target c } 31 } */
+	map[j] = j; /* { dg-warning "statement may fall through" "" { target c++ } 32 } */
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      do /* { dg-warning "statement may fall through" "" { target c++ } 39 } */
+	bar (2);
+      while (--i); /* { dg-warning "statement may fall through" "" { target c } 41 } */
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	switch (i + 2)
+	  case 4:
+	    bar (1); /* { dg-warning "statement may fall through" } */
+	  case 5:
+	    return;
+      }
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:;
+    case 2:;
+    }
+
+  switch (i)
+    {
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i & 1) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (23);
+	  break;
+	}
+    case 2:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 9) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (9);
+	  if (i == 10)
+	    {
+	      bar (10);
+	      break;
+	    }
+	}
+    case 2:;
+    }
+
+  int r;
+  switch (i)
+    {
+      case 1:
+	r = bar2 ();
+	if (r) /* { dg-warning "statement may fall through" } */
+	  break;
+      case 2:;
+    }
+
+  switch (i)
+    {
+      case 1:
+	r = bar2 ();
+	if (r)
+	  return;
+	if (!i) /* { dg-warning "statement may fall through" } */
+	  return;
+      case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-8.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-8.c
index e69de29..508303d 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-8.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-8.c
@@ -0,0 +1,99 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+extern void grace (int);
+
+int
+fn1 (int i)
+{
+  switch (i)
+    case 1:
+    if (i == 5)
+      grace (0);
+    else
+      goto done;
+done:;
+}
+
+int
+fn2 (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i == 5) /* { dg-warning "statement may fall through" } */
+	grace (0);
+      else
+	goto done;
+    case 2:
+      --i;
+    }
+done:;
+}
+
+int
+fn3 (int i)
+{
+  switch (i)
+    {
+    case 1:
+    if (i == 5)
+      goto done;
+    else
+      goto done;
+    }
+done:;
+}
+
+int
+fn4 (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i == 5)
+	{
+	  grace (1);
+	  goto done;
+	}
+      else
+	goto done;
+    case 2:;
+    }
+done:;
+}
+
+int
+fn5 (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i == 5)
+	{
+	  grace (1);
+	  goto done;
+	}
+      else
+	grace (4); /* { dg-warning "statement may fall through" } */
+    case 2:;
+    }
+done:;
+}
+
+int
+fn6 (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i == 5) /* { dg-warning "statement may fall through" } */
+	{
+	  grace (1);
+	  goto done;
+	}
+    case 2:;
+    }
+done:;
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-9.c gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-9.c
index e69de29..b241009 100644
--- gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-9.c
+++ gcc/gcc/testsuite/c-c++-common/Wswitch-fallthrough-9.c
@@ -0,0 +1,26 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-fallthrough" } */
+
+/* Test we don't remove __builtin_fallthrough too early.  */
+
+extern void h (int);
+
+void
+g (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	switch (i)
+	  {
+	    case 3:
+	      h (7);
+	      __builtin_fallthrough ();
+	    case 4:;
+	  }
+      }
+    case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-1.c gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-1.c
index e69de29..6424278 100644
--- gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-1.c
+++ gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-1.c
@@ -0,0 +1,44 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wno-switch-unreachable" } */
+
+extern void g (int);
+void
+f1 (int i)
+{
+  switch (i)
+    {
+      __builtin_fallthrough (); /* { dg-warning "not preceding" } */
+      g (3);
+    case 5:
+      g (5);
+      __builtin_fallthrough (); /* { dg-warning "not preceding" } */
+      g (7);
+      break;
+      __builtin_fallthrough ();
+    case 4:
+      g (9);
+      __builtin_fallthrough ();
+    case 51:
+      {
+	int l;
+        __builtin_fallthrough ();
+      }
+    case 52:
+      __builtin_fallthrough ();
+    }
+}
+
+void
+f2 (int i)
+{
+  if (i)
+    __builtin_fallthrough (); /* { dg-error "invalid use" } */
+}
+
+void
+f3 (int i)
+{
+  switch (i)
+  __builtin_fallthrough ();
+}
diff --git gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-2.c gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-2.c
index e69de29..dd4be9f 100644
--- gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-2.c
+++ gcc/gcc/testsuite/c-c++-common/builtin-fallthrough-2.c
@@ -0,0 +1,16 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+
+extern void g (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 5:
+      g (5);
+      __builtin_fallthrough (1); /* { dg-error "too many" } */
+    default:;
+    }
+}