On Fri, Jul 22, 2016 at 01:06:41PM +0200, Jakub Jelinek wrote:
> On Fri, Jul 22, 2016 at 12:44:07PM +0200, Marek Polacek wrote:
> > --- gcc/gcc/cp/parser.h
> > +++ gcc/gcc/cp/parser.h
> > @@ -46,7 +46,7 @@ struct GTY (()) cp_token {
> >       Otherwise, this value is RID_MAX.  */
> >    ENUM_BITFIELD (rid) keyword : 8;
> >    /* Token flags.  */
> > -  unsigned char flags;
> > +  unsigned short flags;
> >    /* True if this token is from a context where it is implicitly extern 
> > "C" */
> >    BOOL_BITFIELD implicit_extern_c : 1;
> >    /* True if an error has already been reported for this token, such as a
> 
> I'm afraid this is really bad.
> Right now, there are 8 and 8 bit bitfields, then 8-bit char, 3 individual
> bits, 5 unused bits and 32-bit int, nicely packed into 64-bit word before a
> union with pointer members, and the C++ FE lexes everything first, so there
> are possibly millions of tokens in memory.
> Can't you just make it unsigned int flags : 11; instead?  Or instead
> reshuffle the cpplib.h flags?  E.g. I don't see the C++ FE to use the
> NO_EXPAND flag, so moving it to the upper byte of the short and moving the
> new flag to its bit?  Perhaps that is even better for now.

Ouch, sorry, that is embarassing.  I had been meaning to get back to this, but
apparently I never did.  This version should be better.  Another change is that
we don't warn for { something; default: return; } even in nested switches.
Another change is that I made the FEs less permissive wrt invalid code.

CCing Jason and Joseph to see what they think of the FEs part.

2016-07-27  Marek Polacek  <pola...@redhat.com>
            Jakub Jelinek  <ja...@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.
        (last_stmt_in_scope): 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.
        * 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.
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.
        (c_parser_statement_after_labels): Handle RID_ATTRIBUTE.
gcc/cp/
        * constexpr.c (cxx_eval_internal_function): Handle IFN_FALLTHROUGH.
        (potential_constant_expression_1): Likewise.
        * 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.
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-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.
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.

diff --git gcc/gcc/c-family/c-common.c gcc/gcc/c-family/c-common.c
index 1c5974a..e33021a 100644
--- gcc/gcc/c-family/c-common.c
+++ gcc/gcc/c-family/c-common.c
@@ -392,6 +392,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);
@@ -833,6 +834,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 }
 };
 
@@ -9701,6 +9704,19 @@ 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 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/c-parser.c gcc/gcc/c/c-parser.c
index 8952bca..d6a0dd1 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;
@@ -1607,6 +1610,8 @@ static void c_finish_oacc_routine (c_parser *, tree, 
tree, bool, bool, bool);
      declaration-specifiers declarator declaration-list[opt]
        compound-statement
 
+   attribute ;
+
    Objective-C:
      attributes objc-class-definition
      attributes objc-category-definition
@@ -1738,6 +1743,19 @@ 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)
+       {
+         if (is_attribute_p ("fallthrough",
+                             get_attribute_name (specs->attrs)))
+           {
+             tree fn = build_call_expr_internal_loc (here, IFN_FALLTHROUGH,
+                                                     void_type_node, 0);
+             add_stmt (fn);
+           }
+         else
+           error_at (here, "only attribute %<fallthrough%> can be used "
+                     "before %<;%>");
+       }
       else if (empty_ok)
        shadow_tag (specs);
       else
@@ -4960,6 +4978,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;
@@ -5006,6 +5029,10 @@ 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;
       if (c_parser_next_tokens_start_declaration (parser))
        {
          error_at (c_parser_peek_token (parser)->location,
@@ -5059,6 +5086,9 @@ c_parser_label (c_parser *parser)
    jump-statement:
      goto * expression ;
 
+   expression-statement:
+     attributes ;
+
    Objective-C:
 
    statement:
@@ -5320,6 +5350,27 @@ 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 (attrs != NULL_TREE
+               && is_attribute_p ("fallthrough", get_attribute_name (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 used");
+           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 8a292ed..7ba10cb 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 6bcb41a..e93c636 100644
--- gcc/gcc/cp/constexpr.c
+++ gcc/gcc/cp/constexpr.c
@@ -1290,6 +1290,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:
@@ -4718,6 +4719,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/parser.c gcc/gcc/cp/parser.c
index b71b9e5..84bc512 100644
--- gcc/gcc/cp/parser.c
+++ gcc/gcc/cp/parser.c
@@ -5129,6 +5129,31 @@ 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 (attr != NULL_TREE
+               && is_attribute_p ("fallthrough", get_attribute_name (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 used");
+               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
@@ -10558,15 +10583,26 @@ 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
+         && is_attribute_p ("fallthrough", get_attribute_name (std_attrs))
+         && statement == NULL_TREE)
+       {
+         tree fn = build_call_expr_internal_loc (statement_location,
+                                                 IFN_FALLTHROUGH,
+                                                 void_type_node, 0);
+         finish_expr_stmt (fn);
+       }
     }
 
   /* 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.  */
-  if (std_attrs != NULL_TREE)
+  /* Allow "[[fallthrough]];", but warn otherwise.  */
+  if (std_attrs != NULL_TREE
+      && !is_attribute_p ("fallthrough", get_attribute_name (std_attrs)))
     warning_at (attrs_location,
                OPT_Wattributes,
                "attributes at the beginning of statement are ignored");
@@ -10601,6 +10637,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)
     {
@@ -10632,7 +10672,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",
@@ -10645,7 +10689,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;
@@ -10653,6 +10701,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;
     }
 
@@ -24024,6 +24074,16 @@ cp_parser_std_attribute (cp_parser *parser)
                     " use %<gnu::deprecated%>");
          TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
        }
+      /* C++17 fallthrough attribute is equivalent to GNU's.  */
+      else if (cxx_dialect >= cxx11
+              && 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))
@@ -24088,6 +24148,10 @@ cp_parser_check_std_attribute (tree attributes, tree 
attribute)
               && lookup_attribute ("deprecated", attributes))
        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/doc/extend.texi gcc/gcc/doc/extend.texi
index ad8898c..4960d57 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.
@@ -2240,6 +2241,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
@@ -5536,8 +5538,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}).
 
@@ -6318,7 +6320,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
@@ -6828,7 +6831,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
@@ -6881,8 +6885,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:
@@ -6913,6 +6917,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 might appear at most once in each attribute
+list, and might 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
@@ -6940,6 +6984,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}
@@ -7005,6 +7051,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 22001f9..29052f3 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
@@ -3653,6 +3653,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
@@ -3939,6 +3940,91 @@ 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 of a 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 wide range of such comments, so
+e.g. all of "Falls through.", "fallthru", "FALLS-THROUGH" work.
+
+@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 fb27dd0..7529fb2 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,338 @@ maybe_warn_switch_unreachable (gimple_seq seq)
     }
 }
 
+
+/* A label entry that pairs label and a location.  */
+struct label_entry
+{
+  tree label;
+  location_t loc;
+};
+
+/* Find LABEL in vector of label entries VEC.  */
+
+static struct label_entry *
+find_label_entry (const auto_vec <struct label_entry> *vec, tree label)
+{
+  unsigned int i;
+  struct label_entry *l;
+
+  FOR_EACH_VEC_ELT (*vec, i, l)
+    if (l->label == label)
+      return l;
+  return NULL;
+}
+
+/* Find the last statement in a scope STMT.  */
+
+static gimple *
+last_stmt_in_scope (gimple *stmt)
+{
+  if (!stmt)
+    return NULL;
+
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_BIND:
+      {
+       gbind *bind = as_a <gbind *> (stmt);
+       return last_stmt_in_scope (
+                gimple_seq_last_stmt (gimple_bind_body (bind)));
+      }
+
+    case GIMPLE_TRY:
+      {
+       gtry *try_stmt = as_a <gtry *> (stmt);
+       return last_stmt_in_scope (
+                gimple_seq_last_stmt (gimple_try_eval (try_stmt)));
+      }
+
+    default:
+      return stmt;
+    }
+}
+
+/* Callback for walk_gimple_seq.  */
+
+static tree
+warn_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;
+
+       /* Next statements, if any, are non-label.  */
+       gimple *prev = NULL;
+       /* Vector of labels that fall through.  */
+       auto_vec <struct label_entry> labels;
+
+       do
+         {
+           if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND
+               || gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY)
+             {
+               /* Nested scope.  Only look at the last statement of
+                  the innermost scope.  */
+               location_t bind_loc = gimple_location (gsi_stmt (*gsi_p));
+               gimple *last = last_stmt_in_scope (gsi_stmt (*gsi_p));
+               if (last)
+                 {
+                   prev = last;
+                   /* It might be a label without a location.  Use the
+                      location of the scope then.  */
+                   if (!gimple_has_location (prev))
+                     gimple_set_location (prev, bind_loc);
+                 }
+               gsi_next (gsi_p);
+               continue;
+             }
+           /* If's are tricky.  */
+           if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_COND)
+             {
+               gcond *cond_stmt = as_a <gcond *> (gsi_stmt (*gsi_p));
+               tree false_lab = gimple_cond_false_label (cond_stmt);
+               location_t if_loc = gimple_location (cond_stmt);
+
+               /* If we have e.g.
+                  if (i > 1) goto <D.2259>; else goto D;
+                  we can't do much with the else-branch.  */
+               if (!DECL_ARTIFICIAL (false_lab))
+                 break;
+
+               /* Go on until the false label, then one step back.  */
+               for (; !gsi_end_p (*gsi_p); gsi_next (gsi_p))
+                 {
+                   stmt = gsi_stmt (*gsi_p);
+                   if (gimple_code (stmt) == GIMPLE_LABEL
+                       && gimple_label_label (as_a <glabel *> (stmt))
+                            == false_lab)
+                     break;
+                 }
+
+               /* Not found?  Oops.  */
+               if (gsi_end_p (*gsi_p))
+                 break;
+
+               {
+                 struct label_entry l = { false_lab, if_loc };
+                 labels.safe_push (l);
+               }
+
+               /* Go to the last statement of the then branch.  */
+               gsi_prev (gsi_p);
+
+               /* if (i != 0) goto <D.1759>; else goto <D.1760>;
+                  <D.1759>:
+                  <stmt>;
+                  goto <D.1761>;
+                  <D.1760>:
+                */
+               if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+                   && !gimple_has_location (gsi_stmt (*gsi_p)))
+                 {
+                   /* Look at the statement before, it might be
+                      __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))));
+
+       /* 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)
+         {
+           /* Don't warn for terminated branches, i.e. when the subsequent
+              case labels immediately breaks.  */
+           gimple_stmt_iterator gsi2 = *gsi_p;
+           bool fallthru_to_break_p = false;
+
+           /* Skip all immediately following labels.  */
+           while (!gsi_end_p (gsi2)
+                  && gimple_code (gsi_stmt (gsi2)) == GIMPLE_LABEL)
+             gsi_next (&gsi2);
+           /* { ... something; default:; } */
+           if (gsi_end_p (gsi2)
+               /* { ... something; default: break; } or
+                  { ... something; default: goto L; } */
+               || gimple_code (gsi_stmt (gsi2)) == GIMPLE_GOTO
+               /* { ... something; default: return; } */
+               || gimple_code (gsi_stmt (gsi2)) == GIMPLE_RETURN)
+             fallthru_to_break_p = true;
+
+           struct label_entry *l;
+           bool warned_p = false;
+           if (fallthru_to_break_p)
+             /* 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)
+             {
+               rich_location richloc (line_table, gimple_location (next));
+               richloc.add_fixit_insert (gimple_location (next),
+                                         "insert '__attribute__ "
+                                         "((fallthrough));' to silence "
+                                         "this warning");
+               richloc.add_fixit_insert (gimple_location (next),
+                                         "insert 'break;' to avoid "
+                                         "fall-through");
+               inform_at_rich_loc (&richloc, "here");
+             }
+
+           /* 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 || lang_GNU_Fortran ())
+    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;
+         else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL
+                  || (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+                      && !gimple_has_location (gsi_stmt (*gsi_p))))
+           /* This usage is OK.  */;
+         else
+           warning_at (gimple_location (stmt), 0,
+                       "%<__attribute__ ((fallthrough))%> not preceding "
+                       "a 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 +1993,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 innermost 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 +2034,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 +2066,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);
 
@@ -10704,11 +11060,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 49f3495..358757f 100644
--- gcc/gcc/internal-fn.c
+++ gcc/gcc/internal-fn.c
@@ -243,6 +243,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/system.h gcc/gcc/system.h
index 78a7da6..1eba393 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/tree-core.h gcc/gcc/tree-core.h
index 6e8595c..fb1cc8f 100644
--- gcc/gcc/tree-core.h
+++ gcc/gcc/tree-core.h
@@ -1043,6 +1043,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 e2ffabf..52c584f 100644
--- gcc/gcc/tree.h
+++ gcc/gcc/tree.h
@@ -771,6 +771,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 543f3b9..550318a 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 236418d..a806e6c 100644
--- gcc/libcpp/lex.c
+++ gcc/libcpp/lex.c
@@ -2031,6 +2031,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 l = sizeof "fallthrough" - 1;
+      if ((size_t) (pfile->buffer->cur - from - 1) < l)
+       return false;
+      if (memcmp (from + 1, "fallthrough", l))
+       return false;
+      if (*from == '@')
+       {
+         if (from[l + 1] != '@')
+           return false;
+         l++;
+       }
+      from += 1 + l;
+    }
+  /* 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)
@@ -2309,7 +2397,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:
@@ -2336,6 +2424,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;
@@ -2442,6 +2532,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 '\'':
@@ -2533,6 +2628,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;

        Marek

Reply via email to