diff mbox

Implement -Wimplicit-fallthrough (version 9)

Message ID 20160920153343.GF19950@redhat.com
State New
Headers show

Commit Message

Marek Polacek Sept. 20, 2016, 3:33 p.m. UTC
Time for another latest & greatest version of -Wimplicit-fallthrough.
Hope this time it's near its end.

Changes from last time:

* I addressed Jakub's comment, for details see
  <https://gcc.gnu.org/ml/gcc-patches/2016-09/msg01301.html>
* in the C FE:
  case 0:
    __attribute)) ((fallthrough));
  case 1:
    ...
  now works
* we've discussed cases like
  [[fallthrough]]; lab1: case 1:;
  and
  [[fallthrough]]; lab2:; case 2:;

  This was discussed on the C++ committee reflector and it was concluded that
  the former should work and the latter should not.  So I implemented that.
  But there's no way to tell the difference between "lab1: case 1:;" and
  "lab2:; case 2:;" so the null statements are ignored.  Another problem is
  what to do with "[[fallthrough]]; lab1: (void) 0; case 2:".  Currently it's
  not rejected because we optimize "(void) 0;" out in gimplify_expr.  And I
  couldn't figure out how to circumvent this (in gimplify_expr you can't see
  what the next statement is).  Though this seems more like a pedantic issue
  and probably not a real-world issue.  (E.g.
  "[[fallthrough]]; lab1: if (0) { ... } case 2:"
  should be rejected.)
 
Jason, would you mind looking at the C++ FE parts again?

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

2016-09-20  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c/7652
gcc/
	* common.opt (Wimplicit-fallthrough): New option.
	* doc/extend.texi: Document statement attributes and the fallthrough
	attribute.
	* doc/invoke.texi: Document -Wimplicit-fallthrough.
	* gimple.h (gimple_call_internal_p): New function.
	* gimplify.c (struct gimplify_ctx): Add in_switch_expr.
	(struct label_entry): New struct.
	(find_label_entry): New function.
	(case_label_p): New function.
	(collect_fallthrough_labels): New function.
	(last_stmt_in_scope): New function.
	(should_warn_for_implicit_fallthrough): New function.
	(warn_implicit_fallthrough_r): New function.
	(maybe_warn_implicit_fallthrough): New function.
	(expand_FALLTHROUGH_r): New function.
	(expand_FALLTHROUGH): New function.
	(gimplify_switch_expr): Call maybe_warn_implicit_fallthrough and
	expand_FALLTHROUGH for the innermost GIMPLE_SWITCH.
	(gimplify_label_expr): New function.
	(gimplify_case_label_expr): Set location.
	(gimplify_expr): Call gimplify_label_expr.
	* internal-fn.c (expand_FALLTHROUGH): New function.
	* internal-fn.def (FALLTHROUGH): New internal function.
	* langhooks.c (lang_GNU_OBJC): New function.
	* langhooks.h (lang_GNU_OBJC): Declare.
	* system.h (gcc_fallthrough): Define.
	* tree-core.h: Add FALLTHROUGH_LABEL_P comment.
	* tree.h (FALLTHROUGH_LABEL_P): Define.
gcc/c-family/
	* c-common.c (c_common_attribute_table): Add fallthrough attribute.
	(handle_fallthrough_attribute): New function.
	(maybe_attribute_fallthrough_p): New function.
	* c-common.h (maybe_attribute_fallthrough_p): Declare.
gcc/c/
	* c-parser.c (struct c_token): Add flags field.
	(c_lex_one_token): Pass it to c_lex_with_flags.
	(c_parser_declaration_or_fndef): Turn __attribute__((fallthrough));
	into IFN_FALLTHROUGH.
	(c_parser_label): Set FALLTHROUGH_LABEL_P on labels.  Handle
	attribute fallthrough after a case label or default label.
	(c_parser_statement_after_labels): Handle RID_ATTRIBUTE.
gcc/cp/
	* constexpr.c (cxx_eval_internal_function): Handle IFN_FALLTHROUGH.
	(potential_constant_expression_1): Likewise.
	* constraint.cc (function_concept_check_p): Check fn for null.
	* parser.c (cp_parser_primary_expression): Handle RID_ATTRIBUTE.
	(cp_parser_statement): Handle fallthrough attribute.
	(cp_parser_label_for_labeled_statement): Set FALLTHROUGH_LABEL_P on
	labels.
	(cp_parser_std_attribute): Handle fallthrough attribute.
	(cp_parser_check_std_attribute): Detect duplicated fallthrough
	attribute.
	* pt.c (tsubst_copy_and_build): Handle internal functions.
	(instantiation_dependent_scope_ref_p): Return if the expression is
	null.
gcc/testsuite/
	* c-c++-common/Wimplicit-fallthrough-1.c: New test.
	* c-c++-common/Wimplicit-fallthrough-10.c: New test.
	* c-c++-common/Wimplicit-fallthrough-11.c: New test.
	* c-c++-common/Wimplicit-fallthrough-12.c: New test.
	* c-c++-common/Wimplicit-fallthrough-13.c: New test.
	* c-c++-common/Wimplicit-fallthrough-14.c: New test.
	* c-c++-common/Wimplicit-fallthrough-15.c: New test.
	* c-c++-common/Wimplicit-fallthrough-16.c: New test.
	* c-c++-common/Wimplicit-fallthrough-17.c: New test.
	* c-c++-common/Wimplicit-fallthrough-18.c: New test.
	* c-c++-common/Wimplicit-fallthrough-19.c: New test.
	* c-c++-common/Wimplicit-fallthrough-20.c: New test.
	* c-c++-common/Wimplicit-fallthrough-21.c: New test.
	* c-c++-common/Wimplicit-fallthrough-2.c: New test.
	* c-c++-common/Wimplicit-fallthrough-3.c: New test.
	* c-c++-common/Wimplicit-fallthrough-4.c: New test.
	* c-c++-common/Wimplicit-fallthrough-5.c: New test.
	* c-c++-common/Wimplicit-fallthrough-6.c: New test.
	* c-c++-common/Wimplicit-fallthrough-7.c: New test.
	* c-c++-common/Wimplicit-fallthrough-8.c: New test.
	* c-c++-common/Wimplicit-fallthrough-9.c: New test.
	* c-c++-common/attr-fallthrough-1.c: New test.
	* c-c++-common/attr-fallthrough-2.c: New test.
	* g++.dg/cpp0x/fallthrough1.C: New test.
	* g++.dg/cpp0x/fallthrough2.C: New test.
	* g++.dg/cpp1z/fallthrough1.C: New test.
	* gcc.dg/Wimplicit-fallthrough-1.c: New test.
	* obj-c++.dg/Wimplicit-fallthrough-1.mm: New test.
	* objc.dg/Wimplicit-fallthrough-1.m: New test.
libcpp/
	* include/cpplib.h (PREV_FALLTHROUGH): Define.
	* internal.h (CPP_FALLTHRU): Define.
	* lex.c (fallthrough_comment_p): New function.
	(_cpp_lex_direct): Set PREV_FALLTHROUGH on tokens succeeding a falls
	through comment.


	Marek

Comments

Jason Merrill Sept. 20, 2016, 4:26 p.m. UTC | #1
On 09/20/2016 11:33 AM, Marek Polacek wrote:
> @@ -5135,6 +5135,30 @@ cp_parser_primary_expression (cp_parser *parser,
>  	case RID_AT_SELECTOR:
>  	  return cp_parser_objc_expression (parser);
>
> +	case RID_ATTRIBUTE:

Attribute handling doesn't belong in this function; we don't want to 
allow attributes anywhere we see a primary-expression.  This should be 
either in cp_parser_expression_statement or cp_parser_statement.

Jason
diff mbox

Patch

diff --git gcc/gcc/c-family/c-common.c gcc/gcc/c-family/c-common.c
index fc25686..dc132e2 100644
--- gcc/gcc/c-family/c-common.c
+++ gcc/gcc/c-family/c-common.c
@@ -396,6 +396,7 @@  static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
 static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *);
 static tree handle_bnd_legacy (tree *, tree, tree, int, bool *);
 static tree handle_bnd_instrument (tree *, tree, tree, int, bool *);
+static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
 
 static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
 static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT);
@@ -847,6 +848,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_legacy, false },
   { "bnd_instrument",         0, 0, true, false, false,
 			      handle_bnd_instrument, false },
+  { "fallthrough",	      0, 0, false, false, false,
+			      handle_fallthrough_attribute, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
@@ -9840,6 +9843,49 @@  handle_designated_init_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+
+/* Handle a "fallthrough" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_fallthrough_attribute (tree *, tree name, tree, int,
+			      bool *no_add_attrs)
+{
+  warning (OPT_Wattributes, "%qE attribute ignored", name);
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
+/* Check whether ATTR is a valid attribute fallthrough.  */
+
+bool
+maybe_attribute_fallthrough_p (tree attr)
+{
+  tree t = lookup_attribute ("fallthrough", attr);
+  if (t == NULL_TREE)
+    return false;
+  /* This attribute shall appear at most once in each attribute-list.  */
+  if (lookup_attribute ("fallthrough", TREE_CHAIN (t)))
+    {
+      error ("%<fallthrough%> attribute specified multiple times");
+      return false;
+    }
+  /* No attribute-argument-clause shall be present.  */
+  if (TREE_VALUE (t) != NULL_TREE)
+    {
+      error ("%<fallthrough%> attribute specified with a parameter");
+      return false;
+    }
+  /* Warn if other attributes are found.  */
+  for (t = attr; t != NULL_TREE; t = TREE_CHAIN (t))
+    {
+      tree name = get_attribute_name (t);
+      if (!is_attribute_p ("fallthrough", name))
+	warning (OPT_Wattributes, "%qE attribute ignored", name);
+    }
+  return true;
+}
+
 
 /* Check for valid arguments being passed to a function with FNTYPE.
    There are NARGS arguments in the array ARGARRAY.  LOC should be used for
diff --git gcc/gcc/c-family/c-common.h gcc/gcc/c-family/c-common.h
index 5bbf951..b9b42a1 100644
--- gcc/gcc/c-family/c-common.h
+++ gcc/gcc/c-family/c-common.h
@@ -805,6 +805,7 @@  extern void check_function_arguments_recurse (void (*)
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
 					      tree, int, tree *);
 extern void check_function_format (tree, int, tree *);
+extern bool maybe_attribute_fallthrough_p (tree);
 extern tree handle_unused_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
diff --git gcc/gcc/c/c-parser.c gcc/gcc/c/c-parser.c
index 58424a9..95603ca 100644
--- gcc/gcc/c/c-parser.c
+++ gcc/gcc/c/c-parser.c
@@ -193,6 +193,8 @@  struct GTY (()) c_token {
   location_t location;
   /* The value associated with this token, if any.  */
   tree value;
+  /* Token flags.  */
+  unsigned char flags;
 
   source_range get_range () const
   {
@@ -270,7 +272,8 @@  c_lex_one_token (c_parser *parser, c_token *token)
 {
   timevar_push (TV_LEX);
 
-  token->type = c_lex_with_flags (&token->value, &token->location, NULL,
+  token->type = c_lex_with_flags (&token->value, &token->location,
+				  &token->flags,
 				  (parser->lex_untranslated_string
 				   ? C_LEX_STRING_NO_TRANSLATE : 0));
   token->id_kind = C_ID_NONE;
@@ -1288,7 +1291,8 @@  static void c_parser_external_declaration (c_parser *);
 static void c_parser_asm_definition (c_parser *);
 static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool,
 					   bool, bool, tree *, vec<c_token>,
-					   struct oacc_routine_data * = NULL);
+					   struct oacc_routine_data * = NULL,
+					   bool * = NULL);
 static void c_parser_static_assert_declaration_no_semi (c_parser *);
 static void c_parser_static_assert_declaration (c_parser *);
 static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
@@ -1591,6 +1595,8 @@  static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
    attributes; otherwise they may not.
    OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed
    declaration when parsing an Objective-C foreach statement.
+   FALLTHRU_ATTR_P is used to signal whether this function parsed
+   "__attribute__((fallthrough));".
 
    declaration:
      declaration-specifiers init-declarator-list[opt] ;
@@ -1618,6 +1624,8 @@  static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
      declaration-specifiers declarator declaration-list[opt]
        compound-statement
 
+   attribute ;
+
    Objective-C:
      attributes objc-class-definition
      attributes objc-category-definition
@@ -1652,7 +1660,8 @@  c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 			       bool nested, bool start_attr_ok,
 			       tree *objc_foreach_object_declaration,
 			       vec<c_token> omp_declare_simd_clauses,
-			       struct oacc_routine_data *oacc_routine_data)
+			       struct oacc_routine_data *oacc_routine_data,
+			       bool *fallthru_attr_p)
 {
   struct c_declspecs *specs;
   tree prefix_attrs;
@@ -1749,6 +1758,16 @@  c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
     {
       if (auto_type_p)
 	error_at (here, "%<__auto_type%> in empty declaration");
+      else if (specs->typespec_kind == ctsk_none
+	       && specs->attrs
+	       && maybe_attribute_fallthrough_p (specs->attrs))
+	{
+	  if (fallthru_attr_p != NULL)
+	    *fallthru_attr_p = true;
+	  tree fn = build_call_expr_internal_loc (here, IFN_FALLTHROUGH,
+						  void_type_node, 0);
+	  add_stmt (fn);
+	}
       else if (empty_ok)
 	shadow_tag (specs);
       else
@@ -4841,12 +4860,14 @@  c_parser_compound_statement_nostart (c_parser *parser)
 	{
 	  last_label = false;
 	  mark_valid_location_for_stdc_pragma (false);
+	  bool fallthru_attr_p = false;
 	  c_parser_declaration_or_fndef (parser, true, true, true, true,
-					 true, NULL, vNULL);
-	  if (last_stmt)
+					 true, NULL, vNULL, NULL,
+					 &fallthru_attr_p);
+	  if (last_stmt && !fallthru_attr_p)
 	    pedwarn_c90 (loc, OPT_Wdeclaration_after_statement,
 			 "ISO C90 forbids mixed declarations and code");
-	  last_stmt = false;
+	  last_stmt = fallthru_attr_p;
 	}
       else if (!last_label
 	       && c_parser_next_token_is_keyword (parser, RID_EXTENSION))
@@ -4963,6 +4984,11 @@  c_parser_label (c_parser *parser)
 {
   location_t loc1 = c_parser_peek_token (parser)->location;
   tree label = NULL_TREE;
+
+  /* Remember whether this case or a user-defined label is allowed to fall
+     through to.  */
+  bool fallthrough_p = c_parser_peek_token (parser)->flags & PREV_FALLTHROUGH;
+
   if (c_parser_next_token_is_keyword (parser, RID_CASE))
     {
       tree exp1, exp2;
@@ -5009,6 +5035,29 @@  c_parser_label (c_parser *parser)
     }
   if (label)
     {
+      if (TREE_CODE (label) == LABEL_EXPR)
+	FALLTHROUGH_LABEL_P (LABEL_EXPR_LABEL (label)) = fallthrough_p;
+      else
+	FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p;
+
+      /* Allow '__attribute__((fallthrough));'.  */
+      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	{
+	  location_t loc = c_parser_peek_token (parser)->location;
+	  tree attrs = c_parser_attributes (parser);
+	  if (maybe_attribute_fallthrough_p (attrs))
+	    {
+	      tree fn = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH,
+						      void_type_node, 0);
+	      add_stmt (fn);
+	    }
+	  else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    c_parser_error (parser, "only attribute %<fallthrough%> "
+			    "can be applied to a null statement");
+	  else
+	    c_parser_error (parser, "only attribute %<fallthrough%> "
+			    "followed by %<;%> can be used");
+	}
       if (c_parser_next_tokens_start_declaration (parser))
 	{
 	  error_at (c_parser_peek_token (parser)->location,
@@ -5062,6 +5111,9 @@  c_parser_label (c_parser *parser)
    jump-statement:
      goto * expression ;
 
+   expression-statement:
+     attributes ;
+
    Objective-C:
 
    statement:
@@ -5323,6 +5375,26 @@  c_parser_statement_after_labels (c_parser *parser, bool *if_p,
 	  gcc_assert (c_dialect_objc ());
 	  c_parser_objc_synchronized_statement (parser);
 	  break;
+	case RID_ATTRIBUTE:
+	  {
+	    /* Allow '__attribute__((fallthrough));'.  */
+	    tree attrs = c_parser_attributes (parser);
+	    if (maybe_attribute_fallthrough_p (attrs))
+	      {
+		location_t loc = c_parser_peek_token (parser)->location;
+		c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>");
+		tree fn = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH,
+							void_type_node, 0);
+		add_stmt (fn);
+	      }
+	    else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	      c_parser_error (parser, "only attribute %<fallthrough%> "
+			      "can be applied to a null statement");
+	    else
+	      c_parser_error (parser, "only attribute %<fallthrough%> "
+			      "followed by %<;%> can be used");
+	  }
+	  break;
 	default:
 	  goto expr_stmt;
 	}
diff --git gcc/gcc/common.opt gcc/gcc/common.opt
index fa1c036..e8a0154 100644
--- gcc/gcc/common.opt
+++ gcc/gcc/common.opt
@@ -601,6 +601,10 @@  Whsa
 Common Var(warn_hsa) Init(1) Warning
 Warn when a function cannot be expanded to HSAIL.
 
+Wimplicit-fallthrough
+Common Var(warn_implicit_fallthrough) Warning EnabledBy(Wextra)
+Warn when a switch case falls through.
+
 Winline
 Common Var(warn_inline) Warning
 Warn when an inlined function cannot be inlined.
diff --git gcc/gcc/cp/constexpr.c gcc/gcc/cp/constexpr.c
index 34806d6..ecc07a6 100644
--- gcc/gcc/cp/constexpr.c
+++ gcc/gcc/cp/constexpr.c
@@ -1303,6 +1303,7 @@  cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
     case IFN_UBSAN_NULL:
     case IFN_UBSAN_BOUNDS:
     case IFN_UBSAN_VPTR:
+    case IFN_FALLTHROUGH:
       return void_node;
 
     case IFN_ADD_OVERFLOW:
@@ -4829,6 +4830,7 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 		case IFN_UBSAN_NULL:
 		case IFN_UBSAN_BOUNDS:
 		case IFN_UBSAN_VPTR:
+		case IFN_FALLTHROUGH:
 		  return true;
 
 		case IFN_ADD_OVERFLOW:
diff --git gcc/gcc/cp/constraint.cc gcc/gcc/cp/constraint.cc
index 311d025..b4d85c9 100644
--- gcc/gcc/cp/constraint.cc
+++ gcc/gcc/cp/constraint.cc
@@ -116,7 +116,8 @@  function_concept_check_p (tree t)
 {
   gcc_assert (TREE_CODE (t) == CALL_EXPR);
   tree fn = CALL_EXPR_FN (t);
-  if (TREE_CODE (fn) == TEMPLATE_ID_EXPR
+  if (fn != NULL_TREE
+      && TREE_CODE (fn) == TEMPLATE_ID_EXPR
       && TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD)
     {
       tree f1 = get_first_fn (fn);
diff --git gcc/gcc/cp/parser.c gcc/gcc/cp/parser.c
index d704593..253f73e 100644
--- gcc/gcc/cp/parser.c
+++ gcc/gcc/cp/parser.c
@@ -5135,6 +5135,30 @@  cp_parser_primary_expression (cp_parser *parser,
 	case RID_AT_SELECTOR:
 	  return cp_parser_objc_expression (parser);
 
+	case RID_ATTRIBUTE:
+	  {
+	    /* This might be attribute fallthrough.  */
+	    tree attr = cp_parser_gnu_attributes_opt (parser);
+	    if (maybe_attribute_fallthrough_p (attr))
+	      {
+		tree fn = build_call_expr_internal_loc (token->location,
+							IFN_FALLTHROUGH,
+							void_type_node, 0);
+		return cp_expr (fn, token->location);
+	      }
+	    else if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	      {
+		cp_parser_error (parser, "only attribute %<fallthrough%> "
+				 "can be applied to a null statement");
+		return error_mark_node;
+	      }
+	    else
+	      {
+		cp_parser_error (parser, "expected primary-expression");
+		return error_mark_node;
+	      }
+	  }
+
 	case RID_TEMPLATE:
 	  if (parser->in_function_body
 	      && (cp_lexer_peek_nth_token (parser->lexer, 2)->type
@@ -10585,14 +10609,25 @@  cp_parser_statement (cp_parser* parser, tree in_statement_expr,
 	}
       /* Look for an expression-statement instead.  */
       statement = cp_parser_expression_statement (parser, in_statement_expr);
+
+      /* Handle [[fallthrough]];.  */
+      if (std_attrs != NULL_TREE
+	  && maybe_attribute_fallthrough_p (std_attrs)
+	  && statement == NULL_TREE)
+	{
+	  statement = build_call_expr_internal_loc (statement_location,
+						    IFN_FALLTHROUGH,
+						    void_type_node, 0);
+	  finish_expr_stmt (statement);
+	  std_attrs = NULL_TREE;
+	}
     }
 
   /* Set the line number for the statement.  */
   if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
     SET_EXPR_LOCATION (statement, statement_location);
 
-  /* Note that for now, we don't do anything with c++11 statements
-     parsed at this level.  */
+  /* Allow "[[fallthrough]];", but warn otherwise.  */
   if (std_attrs != NULL_TREE)
     warning_at (attrs_location,
 		OPT_Wattributes,
@@ -10628,6 +10663,10 @@  cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
       return;
     }
 
+  /* Remember whether this case or a user-defined label is allowed to fall
+     through to.  */
+  bool fallthrough_p = token->flags & PREV_FALLTHROUGH;
+
   parser->colon_corrects_to_scope_p = false;
   switch (token->keyword)
     {
@@ -10659,7 +10698,11 @@  cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
 	  expr_hi = NULL_TREE;
 
 	if (parser->in_switch_statement_p)
-	  finish_case_label (token->location, expr, expr_hi);
+	  {
+	    tree l = finish_case_label (token->location, expr, expr_hi);
+	    if (l && TREE_CODE (l) == CASE_LABEL_EXPR)
+	      FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p;
+	  }
 	else
 	  error_at (token->location,
 		    "case label %qE not within a switch statement",
@@ -10672,7 +10715,11 @@  cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
       cp_lexer_consume_token (parser->lexer);
 
       if (parser->in_switch_statement_p)
-	finish_case_label (token->location, NULL_TREE, NULL_TREE);
+	{
+	  tree l = finish_case_label (token->location, NULL_TREE, NULL_TREE);
+	  if (l && TREE_CODE (l) == CASE_LABEL_EXPR)
+	    FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p;
+	}
       else
 	error_at (token->location, "case label not within a switch statement");
       break;
@@ -10680,6 +10727,8 @@  cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
     default:
       /* Anything else must be an ordinary label.  */
       label = finish_label_stmt (cp_parser_identifier (parser));
+      if (label && TREE_CODE (label) == LABEL_DECL)
+	FALLTHROUGH_LABEL_P (label) = fallthrough_p;
       break;
     }
 
@@ -24115,7 +24164,7 @@  cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
       if (is_attribute_p ("noreturn", attr_id))
 	TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
       /* C++14 deprecated attribute is equivalent to GNU's.  */
-      else if (cxx_dialect >= cxx11 && is_attribute_p ("deprecated", attr_id))
+      else if (is_attribute_p ("deprecated", attr_id))
 	{
 	  if (cxx_dialect == cxx11)
 	    pedwarn (token->location, OPT_Wpedantic,
@@ -24123,6 +24172,15 @@  cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
 		     " use %<gnu::deprecated%>");
 	  TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
 	}
+      /* C++17 fallthrough attribute is equivalent to GNU's.  */
+      else if (is_attribute_p ("fallthrough", attr_id))
+	{
+	  if (cxx_dialect < cxx1z)
+	    pedwarn (token->location, OPT_Wpedantic,
+		     "%<fallthrough%> is a C++17 feature;"
+		     " use %<gnu::fallthrough%>");
+	  TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
+	}
       /* Transactional Memory TS optimize_for_synchronized attribute is
 	 equivalent to GNU transaction_callable.  */
       else if (is_attribute_p ("optimize_for_synchronized", attr_id))
@@ -24181,11 +24239,15 @@  cp_parser_check_std_attribute (tree attributes, tree attribute)
       tree name = get_attribute_name (attribute);
       if (is_attribute_p ("noreturn", name)
 	  && lookup_attribute ("noreturn", attributes))
-	error ("attribute noreturn can appear at most once "
+	error ("attribute %<noreturn%> can appear at most once "
 	       "in an attribute-list");
       else if (is_attribute_p ("deprecated", name)
 	       && lookup_attribute ("deprecated", attributes))
-	error ("attribute deprecated can appear at most once "
+	error ("attribute %<deprecated%> can appear at most once "
+	       "in an attribute-list");
+      else if (is_attribute_p ("fallthrough", name)
+	       && lookup_attribute ("fallthrough", attributes))
+	error ("attribute %<fallthrough%> can appear at most once "
 	       "in an attribute-list");
     }
 }
diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c
index 29d8beb..a0cbb2e 100644
--- gcc/gcc/cp/pt.c
+++ gcc/gcc/cp/pt.c
@@ -16556,7 +16556,16 @@  tsubst_copy_and_build (tree t,
 	tree ret;
 
 	function = CALL_EXPR_FN (t);
-	/* When we parsed the expression,  we determined whether or
+	if (function == NULL_TREE)
+	  {
+	    /* If you hit this assert, it means that you're trying to tsubst
+	       an internal function with arguments.  This isn't yet supported,
+	       so you need to build another internal call with the tsubsted
+	       arguments after the arguments have been tsubsted down below.  */
+	    gcc_assert (call_expr_nargs (t) == 0);
+	    RETURN (t);
+	  }
+	/* When we parsed the expression, we determined whether or
 	   not Koenig lookup should be performed.  */
 	koenig_p = KOENIG_LOOKUP_P (t);
 	if (TREE_CODE (function) == SCOPE_REF)
@@ -22787,7 +22796,7 @@  instantiation_dependent_scope_ref_p (tree t)
 bool
 value_dependent_expression_p (tree expression)
 {
-  if (!processing_template_decl)
+  if (!processing_template_decl || expression == NULL_TREE)
     return false;
 
   /* A name declared with a dependent type.  */
diff --git gcc/gcc/doc/extend.texi gcc/gcc/doc/extend.texi
index 8df9d62..ff53ab5 100644
--- gcc/gcc/doc/extend.texi
+++ gcc/gcc/doc/extend.texi
@@ -60,6 +60,7 @@  extensions, accepted by GCC in C90 mode and in C++.
 * Type Attributes::     Specifying attributes of types.
 * Label Attributes::    Specifying attributes on labels.
 * Enumerator Attributes:: Specifying attributes on enumerators.
+* Statement Attributes:: Specifying attributes on statements.
 * Attribute Syntax::    Formal syntax for attributes.
 * Function Prototypes:: Prototype declarations and old-style definitions.
 * C++ Comments::        C++ comments are recognized.
@@ -2261,6 +2262,7 @@  GCC also supports attributes on
 variable declarations (@pxref{Variable Attributes}),
 labels (@pxref{Label Attributes}),
 enumerators (@pxref{Enumerator Attributes}),
+statements (@pxref{Statement Attributes}),
 and types (@pxref{Type Attributes}).
 
 There is some overlap between the purposes of attributes and pragmas
@@ -5563,8 +5565,8 @@  attributes are currently defined generically for variables.
 Other attributes are defined for variables on particular target
 systems.  Other attributes are available for functions
 (@pxref{Function Attributes}), labels (@pxref{Label Attributes}),
-enumerators (@pxref{Enumerator Attributes}), and for types
-(@pxref{Type Attributes}).
+enumerators (@pxref{Enumerator Attributes}), statements
+(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}).
 Other front ends might define more attributes
 (@pxref{C++ Extensions,,Extensions to the C++ Language}).
 
@@ -6345,7 +6347,8 @@  attributes of types.  Some type attributes apply only to @code{struct}
 and @code{union} types, while others can apply to any type defined
 via a @code{typedef} declaration.  Other attributes are defined for
 functions (@pxref{Function Attributes}), labels (@pxref{Label 
-Attributes}), enumerators (@pxref{Enumerator Attributes}), and for
+Attributes}), enumerators (@pxref{Enumerator Attributes}), 
+statements (@pxref{Statement Attributes}), and for
 variables (@pxref{Variable Attributes}).
 
 The @code{__attribute__} keyword is followed by an attribute specification
@@ -6855,7 +6858,8 @@  GCC allows attributes to be set on C labels.  @xref{Attribute Syntax}, for
 details of the exact syntax for using attributes.  Other attributes are 
 available for functions (@pxref{Function Attributes}), variables 
 (@pxref{Variable Attributes}), enumerators (@pxref{Enumerator Attributes}),
-and for types (@pxref{Type Attributes}).
+statements (@pxref{Statement Attributes}), and for types
+(@pxref{Type Attributes}).
 
 This example uses the @code{cold} label attribute to indicate the 
 @code{ErrorHandling} branch is unlikely to be taken and that the
@@ -6908,8 +6912,8 @@  with computed goto or @code{asm goto}.
 GCC allows attributes to be set on enumerators.  @xref{Attribute Syntax}, for
 details of the exact syntax for using attributes.  Other attributes are
 available for functions (@pxref{Function Attributes}), variables
-(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}),
-and for types (@pxref{Type Attributes}).
+(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), statements
+(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}).
 
 This example uses the @code{deprecated} enumerator attribute to indicate the
 @code{oldval} enumerator is deprecated:
@@ -6940,6 +6944,46 @@  do instead.  Note that the warnings only occurs for uses.
 
 @end table
 
+@node Statement Attributes
+@section Statement Attributes
+@cindex Statement Attributes
+
+GCC allows attributes to be set on null statements.  @xref{Attribute Syntax},
+for details of the exact syntax for using attributes.  Other attributes are
+available for functions (@pxref{Function Attributes}), variables
+(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators
+(@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}).
+
+This example uses the @code{fallthrough} statement attribute to indicate that
+the @option{-Wimplicit-fallthrough} warning should not be emitted:
+
+@smallexample
+switch (cond)
+  @{
+  case 1:
+    bar (1);
+    __attribute__((fallthrough));
+  case 2:
+    @dots{}
+  @}
+@end smallexample
+
+@table @code
+@item fallthrough
+@cindex @code{fallthrough} statement attribute
+The @code{fallthrough} attribute with a null statement serves as a
+fallthrough statement.  It hints to the compiler that a statement
+that falls through to another case label, or user-defined label
+in a switch statement is intentional and thus the
+@option{-Wimplicit-fallthrough} warning must not trigger.  The
+fallthrough attribute may appear at most once in each attribute
+list, and may not be mixed with other attributes.  It can only
+be used in a switch statement (the compiler will issue an error
+otherwise), after a preceding statement and before a logically
+succeeding case label, or user-defined label.
+
+@end table
+
 @node Attribute Syntax
 @section Attribute Syntax
 @cindex attribute syntax
@@ -6967,6 +7011,8 @@  and enumerated types.
 applying to labels.
 @xref{Enumerator Attributes}, for details of the semantics of attributes
 applying to enumerators.
+@xref{Statement Attributes}, for details of the semantics of attributes
+applying to statements.
 
 An @dfn{attribute specifier} is of the form
 @code{__attribute__ ((@var{attribute-list}))}.  An @dfn{attribute list}
@@ -7032,6 +7078,10 @@  present.  The optional attribute in the enumerator appertains to the
 enumeration constant.  It is not possible to place the attribute after
 the constant expression, if present.
 
+@subsubheading Statement Attributes
+In GNU C, an attribute specifier list may appear as part of a null
+statement.  The attribute goes before the semicolon.
+
 @subsubheading Type Attributes
 
 An attribute specifier list may appear as part of a @code{struct},
diff --git gcc/gcc/doc/invoke.texi gcc/gcc/doc/invoke.texi
index 8eb5eff..76576ff 100644
--- gcc/gcc/doc/invoke.texi
+++ gcc/gcc/doc/invoke.texi
@@ -272,8 +272,8 @@  Objective-C and Objective-C++ Dialects}.
 -Wformat-security  -Wformat-signedness  -Wformat-y2k -Wframe-address @gol
 -Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol
 -Wignored-qualifiers  -Wignored-attributes  -Wincompatible-pointer-types @gol
--Wimplicit  -Wimplicit-function-declaration  -Wimplicit-int @gol
--Winit-self  -Winline  -Wno-int-conversion @gol
+-Wimplicit  -Wimplicit-fallthrough  -Wimplicit-function-declaration  @gol
+-Wimplicit-int  -Winit-self  -Winline  -Wno-int-conversion @gol
 -Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol
 -Winvalid-pch -Wlarger-than=@var{len} @gol
 -Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
@@ -3715,6 +3715,7 @@  name is still supported, but the newer name is more descriptive.)
 @gccoptlist{-Wclobbered  @gol
 -Wempty-body  @gol
 -Wignored-qualifiers @gol
+-Wimplicit-fallthrough @gol
 -Wmissing-field-initializers  @gol
 -Wmissing-parameter-type @r{(C only)}  @gol
 -Wold-style-declaration @r{(C only)}  @gol
@@ -4001,6 +4002,93 @@  enabled by default and it is made into an error by
 Same as @option{-Wimplicit-int} and @option{-Wimplicit-function-declaration}.
 This warning is enabled by @option{-Wall}.
 
+@item -Wimplicit-fallthrough
+@opindex Wimplicit-fallthrough
+@opindex Wno-implicit-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 or a call to function
+declared with the noreturn attribute.  @option{-Wimplicit-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 an attribute, @code{__attribute__ ((fallthrough))}, that is
+to be used along with a null statement to suppress this warning that
+would normally occur:
+
+@smallexample
+@group
+switch (cond)
+  @{
+  case 1:
+    bar (0);
+    __attribute__ ((fallthrough));
+  default:
+    @dots{}
+  @}
+@end group
+@end smallexample
+
+C++17 provides a standard way to suppress the @option{-Wimplicit-fallthrough}
+warning using @code{[[fallthrough]];} instead of the GNU attribute.  In C++11
+or C++14 users can use @code{[[gnu::fallthrough]];}, which is a GNU extension.
+Instead of the these attributes, it is also possible to add a "falls through"
+comment to silence the warning.  GCC accepts a wide range of such comments,
+for example all of "Falls through.", "fallthru", "FALLS-THROUGH" work.  This
+comment needs to consist of two words merely, optionally followed by periods
+or whitespaces.
+
+@smallexample
+@group
+switch (cond)
+  @{
+  case 1:
+    bar (0);
+    /* FALLTHRU */
+  default:
+    @dots{}
+  @}
+@end group
+@end smallexample
+
+This warning is enabled by @option{-Wextra}.
+
 @item -Wignored-qualifiers @r{(C and C++ only)}
 @opindex Wignored-qualifiers
 @opindex Wno-ignored-qualifiers
diff --git gcc/gcc/gimple.h gcc/gcc/gimple.h
index 980bdf8..9fad15b 100644
--- gcc/gcc/gimple.h
+++ gcc/gcc/gimple.h
@@ -2921,6 +2921,16 @@  gimple_call_internal_unique_p (const gimple *gs)
   return gimple_call_internal_unique_p (gc);
 }
 
+/* Return true if GS is an internal function FN.  */
+
+static inline bool
+gimple_call_internal_p (const gimple *gs, internal_fn fn)
+{
+  return (is_gimple_call (gs)
+	  && gimple_call_internal_p (gs)
+	  && gimple_call_internal_fn (gs) == fn);
+}
+
 /* If CTRL_ALTERING_P is true, mark GIMPLE_CALL S to be a stmt
    that could alter control flow.  */
 
diff --git gcc/gcc/gimplify.c gcc/gcc/gimplify.c
index e378ed0..66bb8be 100644
--- gcc/gcc/gimplify.c
+++ gcc/gcc/gimplify.c
@@ -160,6 +160,7 @@  struct gimplify_ctx
   unsigned in_cleanup_point_expr : 1;
   unsigned keep_stack : 1;
   unsigned save_stack : 1;
+  unsigned in_switch_expr : 1;
 };
 
 struct gimplify_omp_ctx
@@ -1626,6 +1627,430 @@  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;
+}
+
+/* Return true if LABEL, a LABEL_DECL, represents a case label
+   in a vector of labels CASES.  */
+
+static bool
+case_label_p (const vec<tree> *cases, tree label)
+{
+  unsigned int i;
+  tree l;
+
+  FOR_EACH_VEC_ELT (*cases, i, l)
+    if (CASE_LABEL (l) == label)
+      return true;
+  return false;
+}
+
+/* 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);
+	stmt = gimple_seq_last_stmt (gimple_bind_body (bind));
+	return last_stmt_in_scope (stmt);
+      }
+
+    case GIMPLE_TRY:
+      {
+	gtry *try_stmt = as_a <gtry *> (stmt);
+	stmt = gimple_seq_last_stmt (gimple_try_eval (try_stmt));
+	gimple *last_eval = last_stmt_in_scope (stmt);
+	if (gimple_stmt_may_fallthru (last_eval)
+	    && gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
+	  {
+	    stmt = gimple_seq_last_stmt (gimple_try_cleanup (try_stmt));
+	    return last_stmt_in_scope (stmt);
+	  }
+	else
+	  return last_eval;
+      }
+
+    default:
+      return stmt;
+    }
+}
+
+/* Collect interesting labels in LABELS and return the statement preceding
+   another case label, or a user-defined label.  */
+
+static gimple *
+collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
+			    auto_vec <struct label_entry> *labels)
+{
+  gimple *prev = NULL;
+
+  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;
+	}
+
+      /* Ifs 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))
+	    {
+	      gimple *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
+		 attribute fallthrough, in which case don't warn.  */
+	      gsi_prev (gsi_p);
+	      bool fallthru_before_dest
+		= gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_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))));
+
+  return prev;
+}
+
+/* Return true if the switch fallthough warning should occur.  LABEL is
+   the label statement that we're falling through to.  */
+
+static bool
+should_warn_for_implicit_fallthrough (gimple_stmt_iterator *gsi_p, tree label)
+{
+  gimple_stmt_iterator gsi = *gsi_p;
+
+  /* Don't warn for a non-case label followed by a statement:
+       case 0:
+	 foo ();
+       label:
+	 bar ();
+     as these are likely intentional.  */
+  if (!case_label_p (&gimplify_ctxp->case_labels, label))
+    {
+      gsi_next (&gsi);
+      if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
+	return false;
+    }
+
+  /* Don't warn for terminated branches, i.e. when the subsequent case labels
+     immediately breaks.  */
+  gsi = *gsi_p;
+
+  /* Skip all immediately following labels.  */
+  while (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+    gsi_next (&gsi);
+
+  /* { ... something; default:; } */
+  if (gsi_end_p (gsi)
+      /* { ... something; default: break; } or
+	 { ... something; default: goto L; } */
+      || gimple_code (gsi_stmt (gsi)) == GIMPLE_GOTO
+      /* { ... something; default: return; } */
+      || gimple_code (gsi_stmt (gsi)) == GIMPLE_RETURN)
+    return false;
+
+  return true;
+}
+
+/* Callback for walk_gimple_seq.  */
+
+static tree
+warn_implicit_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;
+
+	/* Vector of labels that fall through.  */
+	auto_vec <struct label_entry> labels;
+	gimple *prev = collect_fallthrough_labels (gsi_p, &labels);
+
+	/* There might be no more statements.  */
+	if (gsi_end_p (*gsi_p))
+	  return integer_zero_node;
+
+	gimple *next = gsi_stmt (*gsi_p);
+	tree label;
+	/* If what follows is a label, then we may have a fallthrough.  */
+	if (gimple_code (next) == GIMPLE_LABEL
+	    && gimple_has_location (next)
+	    && (label = gimple_label_label (as_a <glabel *> (next)))
+	    && !FALLTHROUGH_LABEL_P (label)
+	    && prev != NULL)
+	  {
+	    struct label_entry *l;
+	    bool warned_p = false;
+	    if (!should_warn_for_implicit_fallthrough (gsi_p, label))
+	      /* Quiet.  */;
+	    else if (gimple_code (prev) == GIMPLE_LABEL
+		     && (label = gimple_label_label (as_a <glabel *> (prev)))
+		     && (l = find_label_entry (&labels, label)))
+	      warned_p = warning_at (l->loc, OPT_Wimplicit_fallthrough,
+				     "this statement may fall through");
+	    else if (!gimple_call_internal_p (prev, IFN_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))
+	      warned_p = warning_at (gimple_location (prev),
+				     OPT_Wimplicit_fallthrough,
+				     "this statement may fall through");
+	    if (warned_p)
+	      inform (gimple_location (next), "here");
+
+	    /* Mark this label as processed so as to prevent multiple
+	       warnings in nested switches.  */
+	    FALLTHROUGH_LABEL_P (label) = true;
+
+	    /* So that next warn_implicit_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_implicit_fallthrough (gimple_seq seq)
+{
+  if (!warn_implicit_fallthrough)
+    return;
+
+  /* This warning is meant for C/C++/ObjC/ObjC++ only.  */
+  if (!(lang_GNU_C ()
+	|| lang_GNU_CXX ()
+	|| lang_GNU_OBJC ()))
+    return;
+
+  struct walk_stmt_info wi;
+  memset (&wi, 0, sizeof (wi));
+  walk_gimple_seq (seq, warn_implicit_fallthrough_r, NULL, &wi);
+}
+
+/* Callback for walk_gimple_seq.  */
+
+static tree
+expand_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_internal_p (stmt, IFN_FALLTHROUGH))
+	{
+	  gsi_remove (gsi_p, true);
+	  if (gsi_end_p (*gsi_p))
+	    return integer_zero_node;
+
+	  bool found = false;
+	  location_t loc = gimple_location (stmt);
+
+	  gimple_stmt_iterator gsi2 = *gsi_p;
+	  stmt = gsi_stmt (gsi2);
+	  if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
+	    {
+	      /* Go on until the artificial label.  */
+	      tree goto_dest = gimple_goto_dest (stmt);
+	      for (; !gsi_end_p (gsi2); gsi_next (&gsi2))
+		{
+		  if (gimple_code (gsi_stmt (gsi2)) == GIMPLE_LABEL
+		      && gimple_label_label (as_a <glabel *> (gsi_stmt (gsi2)))
+			   == goto_dest)
+		    break;
+		}
+
+	      /* Not found?  Stop.  */
+	      if (gsi_end_p (gsi2))
+		break;
+
+	      /* Look one past it.  */
+	      gsi_next (&gsi2);
+	    }
+
+	  /* We're looking for a case label or default label here.  */
+	  while (!gsi_end_p (gsi2))
+	    {
+	      stmt = gsi_stmt (gsi2);
+	      if (gimple_code (stmt) == GIMPLE_LABEL)
+		{
+		  tree label = gimple_label_label (as_a <glabel *> (stmt));
+		  if (gimple_has_location (stmt) && DECL_ARTIFICIAL (label))
+		    {
+		      found = true;
+		      break;
+		    }
+		}
+	      else
+		/* Something other than a label.  That's not expected.  */
+		break;
+	      gsi_next (&gsi2);
+	    }
+	  if (!found)
+	    warning_at (loc, 0, "attribute %<fallthrough%> not preceding "
+			"a case label or default label");
+	}
+      break;
+    default:
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* Expand all FALLTHROUGH () calls in SEQ.  */
+
+static void
+expand_FALLTHROUGH (gimple_seq *seq_p)
+{
+  struct walk_stmt_info wi;
+  memset (&wi, 0, sizeof (wi));
+  walk_gimple_seq_mod (seq_p, expand_FALLTHROUGH_r, NULL, &wi);
+}
+
 
 /* Gimplify a SWITCH_EXPR, and collect the vector of labels it can
    branch to.  */
@@ -1660,10 +2085,17 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
          labels.  Save all the things from the switch body to append after.  */
       saved_labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels.create (8);
+      bool old_in_switch_expr = gimplify_ctxp->in_switch_expr;
+      gimplify_ctxp->in_switch_expr = true;
 
       gimplify_stmt (&SWITCH_BODY (switch_expr), &switch_body_seq);
 
+      gimplify_ctxp->in_switch_expr = old_in_switch_expr;
       maybe_warn_switch_unreachable (switch_body_seq);
+      maybe_warn_implicit_fallthrough (switch_body_seq);
+      /* Only do this for the outermost GIMPLE_SWITCH.  */
+      if (!gimplify_ctxp->in_switch_expr)
+	expand_FALLTHROUGH (&switch_body_seq);
 
       labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels = saved_labels;
@@ -1694,6 +2126,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 +2158,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);
 
@@ -10777,11 +11225,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/internal-fn.c gcc/gcc/internal-fn.c
index c269ca6..029a534 100644
--- gcc/gcc/internal-fn.c
+++ gcc/gcc/internal-fn.c
@@ -244,6 +244,15 @@  expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the lower pass.  */
+
+static void
+expand_FALLTHROUGH (internal_fn, gcall *call)
+{
+  error_at (gimple_location (call),
+	    "invalid use of attribute %<fallthrough%>");
+}
+
 /* Helper function for expand_addsub_overflow.  Return 1
    if ARG interpreted as signed in its precision is known to be always
    positive or 2 if ARG is known to be always negative, or 3 if ARG may
diff --git gcc/gcc/internal-fn.def gcc/gcc/internal-fn.def
index 6701cd9..d4fbdb2 100644
--- gcc/gcc/internal-fn.def
+++ gcc/gcc/internal-fn.def
@@ -195,6 +195,9 @@  DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_COMPLEMENT, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_RESET, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ATOMIC_COMPARE_EXCHANGE, ECF_LEAF | ECF_NOTHROW, NULL)
 
+/* To implement [[fallthrough]].  */
+DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF | ECF_NOTHROW, NULL)
+
 #undef DEF_INTERNAL_INT_FN
 #undef DEF_INTERNAL_FLT_FN
 #undef DEF_INTERNAL_OPTAB_FN
diff --git gcc/gcc/langhooks.c gcc/gcc/langhooks.c
index 538d9f9..79a846c 100644
--- gcc/gcc/langhooks.c
+++ gcc/gcc/langhooks.c
@@ -725,3 +725,12 @@  lang_GNU_Fortran (void)
 {
   return strncmp (lang_hooks.name, "GNU Fortran", 11) == 0;
 }
+
+/* Returns true if the current lang_hooks represents the GNU Objective-C
+   frontend.  */
+
+bool
+lang_GNU_OBJC (void)
+{
+  return strncmp (lang_hooks.name, "GNU Objective-C", 15) == 0;
+}
diff --git gcc/gcc/langhooks.h gcc/gcc/langhooks.h
index c109c8c..cfaee62 100644
--- gcc/gcc/langhooks.h
+++ gcc/gcc/langhooks.h
@@ -546,5 +546,6 @@  extern tree add_builtin_type (const char *name, tree type);
 extern bool lang_GNU_C (void);
 extern bool lang_GNU_CXX (void);
 extern bool lang_GNU_Fortran (void);
- 
+extern bool lang_GNU_OBJC (void);
+
 #endif /* GCC_LANG_HOOKS_H */
diff --git gcc/gcc/system.h gcc/gcc/system.h
index 8a17197..8ca71cf 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() __attribute__((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/Wimplicit-fallthrough-1.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c
index e69de29..b45880f 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c
@@ -0,0 +1,38 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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);
+      __attribute__((fallthrough));
+    case 55:
+      if (n > 10)
+	{
+	  f (3);
+	  break;
+	}
+      else
+	{
+	  f (4);
+	  __attribute__((fallthrough));
+	}
+    case 66:
+      f (5);
+     __attribute__((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/Wimplicit-fallthrough-10.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
index e69de29..99e44d9 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
@@ -0,0 +1,239 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      else
+	break;
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i) /* { dg-warning "statement may fall through" } */
+	bar (2);
+      else if (i > 10)
+	{
+	  bar (3);
+	  __attribute__((fallthrough));
+	}
+      else
+	break;
+      case 2:
+	bar (4);
+    }
+
+  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:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	break;
+      case 2:
+	bar (99);
+    }
+
+  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:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __attribute__((fallthrough));
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __attribute__((fallthrough));
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      else
+	break;
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __attribute__((fallthrough));
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      else
+	bar (2); /* { dg-warning "statement may fall through" } */
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __attribute__((fallthrough));
+	}
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (1);
+	  bar (2);
+	}
+      else
+	__attribute__((fallthrough));
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)  /* { dg-warning "statement may fall through" } */
+	{
+	  bar (0);
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	}
+      else
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  __attribute__((fallthrough));
+	}
+      else if (i > 10)
+	{
+	  bar (1);
+	  break;
+	}
+      else
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i)
+	{
+	  bar (0);
+	  break;
+	}
+      else if (i > 10) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (1);
+	}
+      else
+	{
+	  bar (1);
+	  __attribute__((fallthrough));
+	}
+      case 2:
+	bar (99);
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
index e69de29..e8f47f5 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
@@ -0,0 +1,23 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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/Wimplicit-fallthrough-12.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
index e69de29..91a68ab 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
@@ -0,0 +1,26 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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/Wimplicit-fallthrough-13.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
index e69de29..f3ec79f 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
@@ -0,0 +1,63 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* As per <http://security.coverity.com/blog/2013/Sep/gimme-a-break.html>, don't
+   warn for terminated branches (fall through to break / end of the switch).  */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    default:
+      return;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    default:
+      goto X;
+    }
+X:
+
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    default:
+      break;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    case 2:
+    case 3:
+    default:
+      break;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    default:;
+    }
+
+  switch (i)
+    {
+    case 1:
+      bar (1);
+    case 2:
+    case 3:
+    default:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
index e69de29..b7c825b 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
@@ -0,0 +1,162 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Test various falls through comments.  */
+
+extern void bar (int);
+
+void
+fn (int i)
+{
+  switch (i)
+    {
+    case -1:
+      bar (-1);
+      /*-fallthrough*/
+    case 0:
+      bar (0);
+      /*@fallthrough@*/
+    case 1:
+      bar (1);
+      /* FALL THRU */
+    case 2:
+       bar (2);
+      /* FALLTHRU */
+    case 3:
+      bar (3);
+      /* FALLS THRU */
+    case 4:
+      bar (4);
+      /* FALL-THRU */
+    case 5:
+      bar (5);
+      /* FALL THROUGH */
+    case 6:
+       bar (6);
+      /* FALLTHROUGH */
+    case 7:
+      bar (7);
+      /* FALLS THROUGH */
+    case 8:
+      bar (8);
+      /* FALL-THROUGH */
+    case 9:
+      bar (9);
+      /*FALLTHRU*/
+    case 10:
+      bar (10);
+      /* FALLTHRU.*/
+    case 11:
+       bar (11);
+      /* FALLTHROUGH.  */
+    case 12:
+       bar (12);
+      /* Fall thru */
+    case 13:
+       bar (13);
+      /* Falls thru */
+    case 14:
+       bar (14);
+      /* Fall-thru */
+    case 15:
+       bar (15);
+      /* Fall Thru */
+    case 16:
+       bar (16);
+      /* Falls Thru */
+    case 17:
+       bar (17);
+      /* Fall-Thru */
+    case 18:
+       bar (18);
+      /* Fall through */
+    case 19:
+       bar (19);
+      /* Falls through */
+    case 20:
+       bar (20);
+      /* Fall-through */
+    case 21:
+       bar (21);
+      /* Fall Through */
+    case 22:
+       bar (22);
+      /* Falls Through */
+    case 23:
+       bar (23);
+      /* Fall-Through */
+    case 24:
+       bar (24);
+      /* Falls through.  */
+    case 25:
+       bar (25);
+      /*     Falls through.  */
+    case 26:
+       bar (26);
+      /* fall thru */
+    case 27:
+       bar (27);
+      /* falls thru */
+    case 28:
+       bar (28);
+      /* fall-thru */
+    case 29:
+       bar (29);
+      /* fall thru */
+    case 30:
+       bar (30);
+      /* falls thru */
+    case 31:
+       bar (31);
+      /* fall-thru */
+    case 32:
+       bar (32);
+      /* fall through */
+    case 33:
+       bar (33);
+      /* falls through */
+    case 34:
+       bar (34);
+      /* fall-through */
+    default:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 0:
+      i++;
+      /*@fallthrough@*/
+L:
+    default:
+      bar (6);
+    }
+
+  {
+    __label__ L2;
+    switch (i)
+      {
+      case 0:
+	i++;
+	/*@fallthrough@*/
+L2:
+      default:
+      bar (6);
+      }
+  }
+
+  /* Don't generate false -Wswitch-unreachable warning.  */
+  switch (i)
+    {
+      /*FALLTHROUGH*/
+      case 0:
+        i++;
+    }
+
+  if (i)
+  {
+    /* fall through */
+  L1:;
+  }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
index e69de29..ee3e52d 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
@@ -0,0 +1,31 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch.  Check that we don't warn here.  */
+
+void
+f (int i)
+{
+  int j = 0;
+  switch (i)
+    {
+    case 0:
+    case 1:
+      j = 10;
+      __attribute__((fallthrough));
+    case 2:
+      j += 10;
+      break;
+    case 3:
+      switch (i)
+	{
+	case 5:
+	  j += 2;
+	  __attribute__((fallthrough));
+	case 6:
+	  j += 4;
+	  break;
+	}
+   }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
index e69de29..923f012 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
@@ -0,0 +1,32 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch, and with an initialization on top.  Check that
+   we do warn here.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	int t = 3;
+	switch (i)
+	  {
+	  case 3:
+	    if (i > 5)
+	      --i;
+	    i += 10; /* { dg-warning "statement may fall through" } */
+	  case 4:
+	    t /= 5;
+	    break;
+	  }
+	break;
+      }
+    case 2:
+      --i;
+      break;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
index e69de29..23ff5f1 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
@@ -0,0 +1,29 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch, and with an initialization on top.  Check that
+   we do not warn here as the case 3 falls through to break.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	int t = 3;
+	switch (i)
+	  {
+	  case 3:
+	    i += 10;
+	  case 4:
+	    break;
+	  }
+	break;
+      }
+    case 2:
+      --i;
+      break;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
index e69de29..2c8a3cb 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
@@ -0,0 +1,42 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Testing some loops.  */
+
+int f (void);
+
+int
+g (int i)
+{
+  switch (i)
+    {
+    case 0:
+      for (;;)
+	{
+	  if (f ()) /* { dg-warning "statement may fall through" "fall through" { xfail *-*-* } } */
+	    break;
+	}
+    case 1:
+      return 1;
+    }
+  return 0;
+}
+
+int
+h (int i)
+{
+  switch (i)
+    {
+    case 0:
+      do
+	{
+	  if (f ()) /* { dg-warning "statement may fall through" } */
+	    break;
+	}
+      while (0);
+    case 1:
+      return 1;
+    }
+  return 0;
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
index e69de29..b7a3791 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
@@ -0,0 +1,85 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Testing non-case labels.  */
+
+int foo (int);
+
+void
+f1 (int i)
+{
+  switch (i)
+    {
+    case 0:
+      foo (1);
+    L1:
+      foo (2);
+    }
+
+  switch (i)
+    {
+    case 0:
+      foo (1); /* { dg-warning "statement may fall through" } */
+    L2:
+    case 2:
+      foo (2);
+    }
+
+  switch (i)
+    {
+    case 0:
+      foo (1); /* { dg-warning "statement may fall through" } */
+    case 2:
+    L3:
+      foo (2);
+    }
+
+  switch (i)
+    {
+    case 0:
+      foo (1); /* { dg-warning "statement may fall through" } */
+    L4:
+    case 2:
+    L5:
+      foo (2);
+    }
+
+  switch (i)
+    {
+    case 0:
+      switch (i)
+	{
+	case 1:
+	  foo (2);
+	L6:
+	  foo (3);
+	}
+    }
+
+  switch (i)
+    {
+    case 0:
+      switch (i)
+	{
+	case 1:
+	  foo (2); /* { dg-warning "statement may fall through" } */
+	L7:
+	case 2:
+	  foo (3);
+	}
+    }
+
+  switch (i)
+    {
+    case 0:
+      switch (i)
+	{
+	case 1:
+	  foo (2); /* { dg-warning "statement may fall through" } */
+	case 2:
+	L8:
+	  foo (3);
+	}
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
index e69de29..4dfb278 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
@@ -0,0 +1,223 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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/Wimplicit-fallthrough-20.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
index e69de29..021a019 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
@@ -0,0 +1,41 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+int
+f (int i)
+{
+  switch (i)
+    {
+    case -1:
+      __attribute__((fallthrough));
+    default:
+      __attribute__((fallthrough));
+    case 1:
+      return 6;
+    case 2 ... 4:
+      __attribute__((fallthrough));
+    case 5:
+      return 7;
+    }
+  return 0;
+}
+
+int
+g (int i)
+{
+  switch (i)
+    {
+    case -1:
+      __attribute__((used)); /* { dg-error "only attribute" } */
+    default:
+      __attribute__((used)); /* { dg-error "only attribute" } */
+    case 1:
+      return 6;
+    case 2 ... 4:
+      __attribute__((used)); /* { dg-error "only attribute" } */
+    case 5:
+      return 7;
+    }
+  return 0;
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c
index e69de29..6092a90 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c
@@ -0,0 +1,25 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+int
+f (int i)
+{
+  switch (i)
+    {
+    case 0:
+      i++;
+      __attribute__((fallthrough));
+    lab1:
+    case 1:
+      i++;
+      __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+    lab2:
+      --i;
+      break;
+    case 3:
+      i++;
+      break;
+    }
+  return 0;
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
index e69de29..fbb9712 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
@@ -0,0 +1,543 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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/Wimplicit-fallthrough-4.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
index e69de29..9a0aeb7 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
@@ -0,0 +1,250 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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/Wimplicit-fallthrough-5.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
index e69de29..9317484 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
@@ -0,0 +1,109 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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);
+      __attribute__((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);
+	__attribute__((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/Wimplicit-fallthrough-6.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
index e69de29..8364c1b 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
@@ -0,0 +1,305 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+/* Test nested scopes.  */
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	int j;
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 10; /* { dg-warning "statement may fall through" } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int k = 9;
+	k++;
+	{
+	  int j = 10;
+	  j++; /* { dg-warning "statement may fall through" } */
+	}
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int k = 9;
+	k++;
+	{
+	  int j = 10;
+	  j++;
+	  {
+	    bar (1); /* { dg-warning "statement may fall through" } */
+	  }
+	}
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	__attribute__((fallthrough));
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	{
+	  int k = j + 5;
+	  bar (k);
+	  __attribute__((fallthrough));
+	}
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	return;
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	goto L1;
+      }
+L1:
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 120 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return; /* { dg-warning "statement may fall through" "" { target c++ } 124 } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  return;
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 148 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  return; /* { dg-warning "statement may fall through" "" { target c++ } 154 } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  bar (2); /* { dg-warning "statement may fall through" } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 178 } */
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2); /* { dg-warning "statement may fall through" "" { target c++ } 184 } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+      }
+      break;
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  return;
+      }
+      break;
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  return;
+      }
+      break;
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  return;
+	else
+	  bar (2);
+      }
+      break;
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2);
+      }
+      break;
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 9;
+	while (1);
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 282 } */
+	int j = 9;
+	switch (j); /* { dg-warning "statement may fall through" "" { target c++ } 284 } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	int j = 0;
+	bar (j);
+	if (j == 8)
+	  bar (1);
+	else
+	  bar (2);
+	__attribute__((fallthrough));
+      }
+    case 2:
+      bar (99);
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
index e69de29..21a158c 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
@@ -0,0 +1,124 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      { /* { dg-warning "statement may fall through" "" { target c } 23 } */
+	int a[i]; /* { dg-warning "statement may fall through" "" { target c++ } 24 } */
+      }
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      for (int j = 0; j < 10; j++) /* { dg-warning "statement may fall through" "" { target c } 33 } */
+	map[j] = j; /* { dg-warning "statement may fall through" "" { target c++ } 34 } */
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      do /* { dg-warning "statement may fall through" "" { target c++ } 42 } */
+	bar (2);
+      while (--i); /* { dg-warning "statement may fall through" "" { target c } 44 } */
+    case 2:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      {
+	switch (i + 2)
+	  case 4:
+	    bar (1); /* { dg-warning "statement may fall through" } */
+	  case 5:
+	    bar (5);
+	    return;
+      }
+    case 2:
+      bar (99);
+    }
+
+  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:
+      bar (99);
+    }
+
+  switch (i)
+    {
+    case 1:
+      if (i > 9) /* { dg-warning "statement may fall through" } */
+	{
+	  bar (9);
+	  if (i == 10)
+	    {
+	      bar (10);
+	      break;
+	    }
+	}
+    case 2:
+      bar (99);
+    }
+
+  int r;
+  switch (i)
+    {
+    case 1:
+      r = bar2 ();
+      if (r) /* { dg-warning "statement may fall through" } */
+	break;
+      case 2:
+	bar (99);
+    }
+
+  switch (i)
+    {
+      case 1:
+	r = bar2 ();
+	if (r)
+	  return;
+	if (!i) /* { dg-warning "statement may fall through" } */
+	  return;
+      case 2:
+	bar (99);
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
index e69de29..0ed7928 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
@@ -0,0 +1,101 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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:
+      grace (9);
+    }
+done:;
+}
+
+int
+fn6 (int i)
+{
+  switch (i)
+    {
+    case 1:
+      if (i == 5) /* { dg-warning "statement may fall through" } */
+	{
+	  grace (1);
+	  goto done;
+	}
+    case 2:
+      grace (8);
+    }
+done:;
+}
diff --git gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
index e69de29..394d699 100644
--- gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
+++ gcc/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
@@ -0,0 +1,26 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Test we don't remove FALLTHROUGH () too early.  */
+
+extern void h (int);
+
+void
+g (int i)
+{
+  switch (i)
+    {
+    case 1:
+      {
+	switch (i)
+	  {
+	    case 3:
+	      h (7);
+	      __attribute__((fallthrough));
+	    case 4:;
+	  }
+      }
+    case 2:;
+    }
+}
diff --git gcc/gcc/testsuite/c-c++-common/attr-fallthrough-1.c gcc/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
index e69de29..5bd6a1a 100644
--- gcc/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
+++ gcc/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
@@ -0,0 +1,57 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic" } */
+
+extern void bar (int);
+void
+fn (int i)
+{
+  __attribute__((fallthrough)) int j = 0; /* { dg-warning "attribute ignored" } */
+
+  if (j)
+    __attribute__((fallthrough));  /* { dg-error "invalid use" } */
+
+  __attribute__((fallthrough));  /* { dg-error "invalid use" } */
+  switch (i)
+  {
+    __attribute__((fallthrough)); /* { dg-warning "statement will never" } */
+  case 1:
+   i++;
+   __attribute__((fallthrough));
+  case 2:
+    if (i) /* { dg-warning "statement may fall through" } */
+      bar (2);
+    else
+      __attribute__((fallthrough));
+  case 3:
+    if (i > 1)
+      __attribute__((fallthrough));
+    else
+      return;
+  case 4:
+    if (i)
+      __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+    __attribute__((fallthrough));
+  case 5:
+   ;
+   __attribute__((fallthrough));
+  case 6:
+    if (i) /* { dg-warning "statement may fall through" } */
+      bar (6);
+    else
+      {
+	__attribute__((fallthrough));
+      }
+  case 7:
+    if (i > 1)
+      {
+	__attribute__((fallthrough));
+      }
+    else
+      bar (7); /* { dg-warning "statement may fall through" } */
+  default:
+    --j;
+  }
+
+  __attribute__((fallthrough)); /* { dg-error "invalid use" } */
+}
diff --git gcc/gcc/testsuite/c-c++-common/attr-fallthrough-2.c gcc/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
index e69de29..db031cb 100644
--- gcc/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
+++ gcc/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
@@ -0,0 +1,57 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic -Wno-unused -Wno-implicit-fallthrough" } */
+
+extern void bar (int);
+void
+fn (int i)
+{
+  switch (i)
+  {
+  case 1:
+    bar (1);
+    __attribute__((used));
+    /* { dg-warning "empty declaration" "" { target c } 13 } */
+    /* { dg-error "only attribute .fallthrough." "" { target c++ } 13 } */
+  case 2:
+    bar (1);
+    __attribute__((foo));
+    /* { dg-error "only attribute .fallthrough." "" { target c++ } 18 } */
+    /* { dg-warning "empty declaration" "" { target c } 18 } */
+  case 3:
+    bar (1);
+    __attribute__((fallthrough))
+    /* { dg-warning "not preceding" "" { target c++ } 23 } */
+  case 4: /* { dg-error "expected" } */
+    bar (1);
+    __attribute__((fallthrough)) 1; /* { dg-error "expected" } */
+  case 5:
+    bar (1);
+    __attribute__((fallthrough)) int i; /* { dg-warning "ignored" } */
+  case 6:
+    bar (1);
+    __attribute__((fallthrough ("x"))); /* { dg-error "attribute specified with a parameter" } */
+    /* { dg-error "only attribute .fallthrough." "" { target c++ } 33 } */
+    /* { dg-warning "empty declaration" "" { target c } 33 } */
+  case 7:
+    bar (1);
+    __attribute__((fallthrough, fallthrough)); /* { dg-error "attribute specified multiple times" } */
+    /* { dg-error "only attribute .fallthrough." "" { target c++ } 38 } */
+    /* { dg-warning "empty declaration" "" { target c } 38 } */
+  case 8:
+    bar (1);
+    __attribute__((fallthrough));
+  case 9:
+    __attribute__((fallthrough));
+    /* { dg-warning "not preceding" "" { target *-*-* } 45 } */
+    bar (1);
+  case 10:
+    bar (1);
+    __attribute__((unused, fallthrough)); /* { dg-warning "attribute ignored" } */
+  case 11:
+    bar (1);
+    __attribute__((fallthrough, unused)); /* { dg-warning "attribute ignored" } */
+  default:
+    bar (99);
+  }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C
index e69de29..523067e 100644
--- gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C
+++ gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C
@@ -0,0 +1,57 @@ 
+// PR c/7652
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wextra -Wall -Wpedantic" }
+
+extern void bar (int);
+void
+fn (int i)
+{
+  [[gnu::fallthrough]] int j = 0; // { dg-warning "attribute ignored" }
+
+  if (j)
+    [[gnu::fallthrough]];  // { dg-error "invalid use" }
+
+  [[gnu::fallthrough]];  // { dg-error "invalid use" }
+  switch (i)
+  {
+    [[gnu::fallthrough]]; // { dg-warning "statement will never" }
+  case 1:
+   i++;
+   [[gnu::fallthrough]];
+  case 2:
+    if (i) // { dg-warning "statement may fall through" }
+      bar (2);
+    else
+      [[gnu::fallthrough]];
+  case 3:
+    if (i > 1)
+      [[gnu::fallthrough]];
+    else
+      return;
+  case 4:
+    if (i)
+      [[gnu::fallthrough]]; // { dg-warning "not preceding" }
+    [[gnu::fallthrough]];
+  case 5:
+   ;
+   [[gnu::fallthrough]];
+  case 6:
+    if (i) // { dg-warning "statement may fall through" }
+      bar (6);
+    else
+      {
+	[[gnu::fallthrough]];
+      }
+  case 7:
+    if (i > 1)
+      {
+	[[gnu::fallthrough]];
+      }
+    else
+      bar (7); // { dg-warning "statement may fall through" }
+  default:
+    --j;
+  }
+
+  [[gnu::fallthrough]]; // { dg-error "invalid use" }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
index e69de29..1ecaf3e 100644
--- gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
+++ gcc/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
@@ -0,0 +1,21 @@ 
+// PR c/7652
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wextra -Wall -Wpedantic" }
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      bar (1);
+      [[fallthrough]]; // { dg-warning ".fallthrough. is a C\\+\\+17 feature" }
+    case 3:
+      bar (1);
+      [[gnu::fallthrough, gnu::fallthrough]]; // { dg-error ".fallthrough. can appear at most once" }
+    case 2:
+      bar (2);
+    }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C gcc/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C
index e69de29..d15b1ea 100644
--- gcc/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C
+++ gcc/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C
@@ -0,0 +1,20 @@ 
+// PR c/7652
+// { dg-do compile }
+// { dg-options "-std=c++1z -Wextra -Wall -Wpedantic" }
+
+// Check that we accept attribute [[fallthrough]].
+
+extern void bar (int);
+
+void
+f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      bar (1);
+      [[fallthrough]];
+    case 2:
+      bar (2);
+    }
+}
diff --git gcc/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c gcc/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c
index e69de29..f8b54f5 100644
--- gcc/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c
+++ gcc/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c
@@ -0,0 +1,22 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough -Wdeclaration-after-statement" } */
+
+/* Test we don't print bogus "mixed declarations and code" warning.  */
+
+int
+f (int b)
+{
+  switch (b)
+    {
+    case 0:
+      b++;
+      __attribute__((fallthrough));
+    case 1:
+      b--;
+      __attribute__((unused)) int a; /* { dg-warning "mixed declarations and code" } */
+    case 2:
+      break;
+    }
+  return 99;
+}
diff --git gcc/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm gcc/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
index e69de29..b45880f 100644
--- gcc/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
+++ gcc/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
@@ -0,0 +1,38 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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);
+      __attribute__((fallthrough));
+    case 55:
+      if (n > 10)
+	{
+	  f (3);
+	  break;
+	}
+      else
+	{
+	  f (4);
+	  __attribute__((fallthrough));
+	}
+    case 66:
+      f (5);
+     __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+      f (6); /* { dg-warning "statement may fall through" } */
+    case 77:
+       f (7);
+    }
+}
diff --git gcc/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m gcc/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
index e69de29..b45880f 100644
--- gcc/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
+++ gcc/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
@@ -0,0 +1,38 @@ 
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-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);
+      __attribute__((fallthrough));
+    case 55:
+      if (n > 10)
+	{
+	  f (3);
+	  break;
+	}
+      else
+	{
+	  f (4);
+	  __attribute__((fallthrough));
+	}
+    case 66:
+      f (5);
+     __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+      f (6); /* { dg-warning "statement may fall through" } */
+    case 77:
+       f (7);
+    }
+}
diff --git gcc/gcc/tree-core.h gcc/gcc/tree-core.h
index 8b3e5cc..353a625 100644
--- gcc/gcc/tree-core.h
+++ gcc/gcc/tree-core.h
@@ -1077,6 +1077,9 @@  struct GTY(()) tree_base {
        TRANSACTION_EXPR_RELAXED in
 	   TRANSACTION_EXPR
 
+       FALLTHROUGH_LABEL_P in
+	   LABEL_DECL
+
    private_flag:
 
        TREE_PRIVATE in
diff --git gcc/gcc/tree.h gcc/gcc/tree.h
index 38ee816..0d9ad01 100644
--- gcc/gcc/tree.h
+++ gcc/gcc/tree.h
@@ -774,6 +774,11 @@  extern void omp_clause_range_check_failed (const_tree, const char *, int,
    computed gotos.  */
 #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
 
+/* Whether a case or a user-defined label is allowed to fall through to.
+   This is used to implement -Wimplicit-fallthrough.  */
+#define FALLTHROUGH_LABEL_P(NODE) \
+  (LABEL_DECL_CHECK (NODE)->base.public_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.
diff --git gcc/libcpp/include/cpplib.h gcc/libcpp/include/cpplib.h
index cfc6ccd..6352ac5 100644
--- gcc/libcpp/include/cpplib.h
+++ gcc/libcpp/include/cpplib.h
@@ -185,7 +185,8 @@  struct GTY(()) cpp_string {
 #define STRINGIFY_ARG	(1 << 2) /* If macro argument to be stringified.  */
 #define PASTE_LEFT	(1 << 3) /* If on LHS of a ## operator.  */
 #define NAMED_OP	(1 << 4) /* C++ named operators.  */
-#define NO_EXPAND	(1 << 5) /* Do not macro-expand this token.  */
+#define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH
+				     comment.  */
 #define BOL		(1 << 6) /* Token at beginning of line.  */
 #define PURE_ZERO	(1 << 7) /* Single 0 digit, used by the C++ frontend,
 				    set in c-lex.c.  */
@@ -193,6 +194,7 @@  struct GTY(()) cpp_string {
 #define SP_PREV_WHITE	(1 << 9) /* If whitespace before a ##
 				    operator, or before this token
 				    after a # operator.  */
+#define NO_EXPAND	(1 << 10) /* Do not macro-expand this token.  */
 
 /* Specify which field, if any, of the cpp_token union is used.  */
 
diff --git gcc/libcpp/lex.c gcc/libcpp/lex.c
index 6254ed6..0c47e29 100644
--- gcc/libcpp/lex.c
+++ gcc/libcpp/lex.c
@@ -2032,6 +2032,94 @@  save_comment (cpp_reader *pfile, cpp_token *token, const unsigned char *from,
   store_comment (pfile, token);
 }
 
+/* Returns true if comment at COMMENT_START is a recognized FALLTHROUGH
+   comment.  */
+
+static bool
+fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
+{
+  const unsigned char *from = comment_start + 1;
+  /* Whole comment contents:
+     -fallthrough
+     @fallthrough@
+   */
+  if (*from == '-' || *from == '@')
+    {
+      size_t len = sizeof "fallthrough" - 1;
+      if ((size_t) (pfile->buffer->cur - from - 1) < len)
+	return false;
+      if (memcmp (from + 1, "fallthrough", len))
+	return false;
+      if (*from == '@')
+	{
+	  if (from[len + 1] != '@')
+	    return false;
+	  len++;
+	}
+      from += 1 + len;
+    }
+  /* Whole comment contents (regex):
+     [ \t]*FALL(S | |-)?THR(OUGH|U)\.?[ \t]*
+     [ \t]*Fall(s | |-)?[Tt]hr(ough|u)\.?[ \t]*
+     [ \t]*fall(s | |-)?thr(ough|u)\.?[ \t]*
+   */
+  else
+    {
+      while (*from == ' ' || *from == '\t')
+	from++;
+      unsigned char f = *from;
+      if (f != 'F' && f != 'f')
+	return false;
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthrough")
+	return false;
+      bool all_upper = false;
+      if (f == 'F' && memcmp (from + 1, "ALL", sizeof "ALL" - 1) == 0)
+	all_upper = true;
+      else if (memcmp (from + 1, "all", sizeof "all" - 1))
+	return false;
+      if (from[sizeof "fall" - 1] == (all_upper ? 'S' : 's')
+	  && from[sizeof "falls" - 1] == ' ')
+	from += sizeof "falls " - 1;
+      else if (from[sizeof "fall" - 1] == ' '
+	       || from[sizeof "fall" - 1] == '-')
+	from += sizeof "fall " - 1;
+      else if (from[sizeof "fall" - 1] != (all_upper ? 'T' : 't'))
+	return false;
+      else
+	from += sizeof "fall" - 1;
+      if ((f == 'f' || *from != 'T') && (all_upper || *from != 't'))
+	return false;
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "thru")
+	return false;
+      if (memcmp (from + 1, all_upper ? "HRU" : "hru", sizeof "hru" - 1))
+	{
+	  if ((size_t) (pfile->buffer->cur - from) < sizeof "through")
+	    return false;
+	  if (memcmp (from + 1, all_upper ? "HROUGH" : "hrough",
+		      sizeof "hrough" - 1))
+	    return false;
+	  from += sizeof "through" - 1;
+	}
+      else
+	from += sizeof "thru" - 1;
+      if (*from == '.')
+	from++;
+      while (*from == ' ' || *from == '\t')
+	from++;
+    }
+  /* C block comment.  */
+  if (*comment_start == '*')
+    {
+      if (*from != '*' || from[1] != '/')
+	return false;
+    }
+  /* C++ line comment.  */
+  else if (*from != '\n')
+    return false;
+
+  return true;
+}
+
 /* Allocate COUNT tokens for RUN.  */
 void
 _cpp_init_tokenrun (tokenrun *run, unsigned int count)
@@ -2310,7 +2398,7 @@  _cpp_lex_direct (cpp_reader *pfile)
 {
   cppchar_t c;
   cpp_buffer *buffer;
-  const unsigned char *comment_start;
+  const unsigned char *comment_start = NULL;
   cpp_token *result = pfile->cur_token++;
 
  fresh_line:
@@ -2337,6 +2425,8 @@  _cpp_lex_direct (cpp_reader *pfile)
 	    }
 	  return result;
 	}
+      if (buffer != pfile->buffer)
+	comment_start = NULL;
       if (!pfile->keep_tokens)
 	{
 	  pfile->cur_run = &pfile->base_run;
@@ -2443,6 +2533,11 @@  _cpp_lex_direct (cpp_reader *pfile)
 	  result->flags |= NAMED_OP;
 	  result->type = (enum cpp_ttype) result->val.node.node->directive_index;
 	}
+
+      /* Signal FALLTHROUGH comment followed by another token.  */
+      if (comment_start
+	  && fallthrough_comment_p (pfile, comment_start))
+	result->flags |= PREV_FALLTHROUGH;
       break;
 
     case '\'':
@@ -2534,6 +2629,9 @@  _cpp_lex_direct (cpp_reader *pfile)
 	  goto update_tokens_line;
 	}
 
+      if (fallthrough_comment_p (pfile, comment_start))
+	result->flags |= PREV_FALLTHROUGH;
+
       /* Save the comment as a token in its own right.  */
       save_comment (pfile, result, comment_start, c);
       break;