Implement -Wimplicit-fallthrough.
authorMarek Polacek <polacek@redhat.com>
Mon, 26 Sep 2016 09:42:50 +0000 (09:42 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Mon, 26 Sep 2016 09:42:50 +0000 (09:42 +0000)
Co-Authored-By: Jakub Jelinek <jakub@redhat.com>
From-SVN: r240485

83 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/builtins.c
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-parser.c
gcc/common.opt
gcc/config/rs6000/rs6000.c
gcc/convert.c
gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/constraint.cc
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/cp/typeck.c
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/final.c
gcc/fortran/ChangeLog
gcc/fortran/arith.c
gcc/fortran/frontend-passes.c
gcc/fortran/parse.c
gcc/fortran/primary.c
gcc/fortran/trans-array.c
gcc/fortran/trans-expr.c
gcc/fortran/trans-io.c
gcc/genattrtab.c
gcc/genpreds.c
gcc/gimple-ssa-strength-reduction.c
gcc/gimple.h
gcc/gimplify.c
gcc/godump.c
gcc/internal-fn.c
gcc/internal-fn.def
gcc/langhooks.c
gcc/langhooks.h
gcc/reload1.c
gcc/resource.c
gcc/system.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/attr-fallthrough-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/attr-fallthrough-2.c [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/fallthrough1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/fallthrough2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/fallthrough1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wunused-label-1.C
gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c [new file with mode: 0644]
gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm [new file with mode: 0644]
gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m [new file with mode: 0644]
gcc/tree-core.h
gcc/tree-ssa-loop-ivopts.c
gcc/tree.h
gcc/varasm.c
libcpp/ChangeLog
libcpp/include/cpplib.h
libcpp/lex.c
libstdc++-v3/ChangeLog
libstdc++-v3/libsupc++/hash_bytes.cc

index 8481198269faf37641c93f6f5870abf96efb5c1e..d30a7cfdee5d953af66a47a7904631ac68b231f6 100644 (file)
@@ -1,3 +1,59 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * Makefile.in (insn-attrtab.o-warn, insn-dfatab.o-warn,
+       insn-latencytab.o-warn, insn-output.o-warn, insn-emit.o-warn): Add
+       -Wno-switch-fallthrough.
+       * builtins.c (expand_builtin_int_roundingfn_2): Add gcc_fallthrough.
+       (expand_builtin): Likewise.
+       * config/rs6000/rs6000.c (rs6000_builtin_vectorized_libmass): Likewise.
+       * convert.c (convert_to_real_1): Likewise.
+       (convert_to_integer_1): Likewise.
+       * final.c (output_alternate_entry_point): Likewise.
+       * genattrtab.c (make_canonical): Likewise.
+       (write_test_expr): Likewise.
+       * genpreds.c (validate_exp): Likewise.
+       * gimple-ssa-strength-reduction.c
+       (find_candidates_dom_walker::before_dom_children): Likewise.
+       * godump.c (go_format_type): Likewise.
+       * reload1.c (elimination_effects): Likewise.
+       * resource.c (mark_referenced_resources): Likewise.
+       (mark_set_resources): Likewise.
+       * tree-ssa-loop-ivopts.c (find_deriving_biv_for_expr): Likewise.
+       * varasm.c (output_addressed_constants): Likewise.
+
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * 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.
+
 2016-09-26  Richard Biener  <rguenther@suse.de>
 
        * dwarf2out.c (stripattributes): Remove unused function.
index 69ff9fa988da9010b7b68e79964997ee3018b37d..e8559cba02383206cad0c4fafa87cc268e373390 100644 (file)
@@ -218,6 +218,11 @@ libgcov-merge-tool.o-warn = -Wno-error
 gimple-match.o-warn = -Wno-unused
 generic-match.o-warn = -Wno-unused
 dfp.o-warn = -Wno-strict-aliasing
+insn-attrtab.o-warn = -Wno-implicit-fallthrough
+insn-dfatab.o-warn = -Wno-implicit-fallthrough
+insn-latencytab.o-warn = -Wno-implicit-fallthrough
+insn-output.o-warn = -Wno-implicit-fallthrough
+insn-emit.o-warn = -Wno-implicit-fallthrough
 
 # All warnings have to be shut off in stage1 if the compiler used then
 # isn't gcc; configure determines that.  WARN_CFLAGS will be either
index 9a19a75cc8ed6edb5f543cd7bd26bcc0693e6ebb..93cbe15ad3c40c19ccc47f772582fbadc09e7cf7 100644 (file)
@@ -2586,7 +2586,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
     {
     CASE_FLT_FN (BUILT_IN_IRINT):
       fallback_fn = BUILT_IN_LRINT;
-      /* FALLTHRU */
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_LRINT):
     CASE_FLT_FN (BUILT_IN_LLRINT):
       builtin_optab = lrint_optab;
@@ -2594,7 +2594,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
 
     CASE_FLT_FN (BUILT_IN_IROUND):
       fallback_fn = BUILT_IN_LROUND;
-      /* FALLTHRU */
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_LROUND):
     CASE_FLT_FN (BUILT_IN_LLROUND):
       builtin_optab = lround_optab;
@@ -5901,6 +5901,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     CASE_FLT_FN (BUILT_IN_ILOGB):
       if (! flag_unsafe_math_optimizations)
        break;
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_ISINF):
     CASE_FLT_FN (BUILT_IN_FINITE):
     case BUILT_IN_ISFINITE:
index 8bcbd061407b0a4c82eb69696f44d225bfd6ea37..cd3eeab02bdf699e253bc0b2ac759b12d659a6c9 100644 (file)
@@ -1,3 +1,11 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * c-common.c (c_common_attribute_table): Add fallthrough attribute.
+       (handle_fallthrough_attribute): New function.
+       (attribute_fallthrough_p): New function.
+       * c-common.h (attribute_fallthrough_p): Declare.
+
 2016-09-24  Marek Polacek  <polacek@redhat.com>
 
        PR c/77490
index 48d8b054451babc8884e23b7d85a234684bc806d..e9f619fd3af4d4ce39d83ebaa1a5a390246ff5f9 100644 (file)
@@ -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 }
 };
 
@@ -9855,6 +9858,45 @@ 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
+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)))
+    warning (OPT_Wattributes, "%<fallthrough%> attribute specified multiple "
+            "times");
+  /* No attribute-argument-clause shall be present.  */
+  else if (TREE_VALUE (t) != NULL_TREE)
+    warning (OPT_Wattributes, "%<fallthrough%> attribute specified with "
+            "a parameter");
+  /* 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;
+}
+
 \f
 /* Check for valid arguments being passed to a function with FNTYPE.
    There are NARGS arguments in the array ARGARRAY.  LOC should be used for
index 5bbf9511d0869b9126f1916ffa1aca125915d9a9..c88619b5acaf0e663b9549a5bae7ee3e0aee058d 100644 (file)
@@ -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 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 *);
index cae5c922067d99a4a4a3010799bfe1811455178a..642c20ca553e617529c8c99f20634f35a60fd074 100644 (file)
@@ -1,3 +1,19 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * c-decl.c (pop_scope): Add gcc_fallthrough.
+
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * 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.
+
 2016-09-24  Marek Polacek  <polacek@redhat.com>
 
        PR c/77490
index d15b8f89b0af93476dff26f1a0da68349fe29897..9e32be2e8ed1be7eab8ab3cf5d86524a6b4913e7 100644 (file)
@@ -1328,7 +1328,7 @@ pop_scope (void)
                set_type_context (TREE_TYPE (p), context);
            }
 
-         /* Fall through.  */
+         gcc_fallthrough ();
          /* Parameters go in DECL_ARGUMENTS, not BLOCK_VARS, and have
             already been put there by store_parm_decls.  Unused-
             parameter warnings are handled by function.c.
index 5f610e9ad7b5dee2ce4d92a00c97dddae43e7620..6bc42da08d64206b849db834ed1f3d6153c5d976 100644 (file)
@@ -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,15 @@ 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
+              && 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
@@ -1851,7 +1869,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
          break;
        }
     }
-  
+  else if (attribute_fallthrough_p (specs->attrs))
+    warning_at (here, OPT_Wattributes,
+               "%<fallthrough%> attribute not followed by %<;%>");
+
   pending_xref_error ();
   prefix_attrs = specs->attrs;
   all_prefix_attrs = prefix_attrs;
@@ -4841,12 +4862,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 +4986,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 +5037,33 @@ 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 (attribute_fallthrough_p (attrs))
+           {
+             if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+               {
+                 tree fn = build_call_expr_internal_loc (loc,
+                                                         IFN_FALLTHROUGH,
+                                                         void_type_node, 0);
+                 add_stmt (fn);
+               }
+             else
+               warning_at (loc, OPT_Wattributes, "%<fallthrough%> attribute "
+                           "not followed by %<;%>");
+           }
+         else if (attrs != NULL_TREE)
+           warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
+                       " can be applied to a null statement");
+       }
       if (c_parser_next_tokens_start_declaration (parser))
        {
          error_at (c_parser_peek_token (parser)->location,
@@ -5062,6 +5117,9 @@ c_parser_label (c_parser *parser)
    jump-statement:
      goto * expression ;
 
+   expression-statement:
+     attributes ;
+
    Objective-C:
 
    statement:
@@ -5323,6 +5381,31 @@ 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 (attribute_fallthrough_p (attrs))
+             {
+               if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+                 {
+                   tree fn = build_call_expr_internal_loc (loc,
+                                                           IFN_FALLTHROUGH,
+                                                           void_type_node, 0);
+                   add_stmt (fn);
+                   /* Eat the ';'.  */
+                   c_parser_consume_token (parser);
+                 }
+               else
+                 warning_at (loc, OPT_Wattributes,
+                             "%<fallthrough%> attribute not followed "
+                             "by %<;%>");
+             }
+           else if (attrs != NULL_TREE)
+             warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
+                         " can be applied to a null statement");
+           break;
+         }
        default:
          goto expr_stmt;
        }
index 8c0885c57f76683afb4bd7ccaae43e4e98657922..b1d32fb45a25f66207378b55993eed8fee4dcdee 100644 (file)
@@ -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.
index 5d189fc991a9712f19449538b5b2cce8840f8031..d76f479d9e59fa140a8a3d6cff0ad5a40788fc6c 100644 (file)
@@ -5489,7 +5489,7 @@ rs6000_builtin_vectorized_libmass (combined_fn fn, tree type_out,
     CASE_CFN_HYPOT:
     CASE_CFN_POW:
       n_args = 2;
-      /* fall through */
+      gcc_fallthrough ();
 
     CASE_CFN_ACOS:
     CASE_CFN_ACOSH:
index e6b4d295c4ea393f869c74ff0d15e4c845409a0e..8f18ee4d2477e88d926c2eecaed6e09e3c6f3fca 100644 (file)
@@ -164,6 +164,7 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
               -fmath-errno.  */
            if (flag_errno_math)
              break;
+           gcc_fallthrough ();
          CASE_MATHFN (ACOS)
          CASE_MATHFN (ACOSH)
          CASE_MATHFN (ASIN)
@@ -184,6 +185,7 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
            /* The above functions are not safe to do this conversion.  */
            if (!flag_unsafe_math_optimizations)
              break;
+           gcc_fallthrough ();
          CASE_MATHFN (SQRT)
          CASE_MATHFN (FABS)
          CASE_MATHFN (LOGB)
@@ -516,7 +518,7 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
          /* Only convert nearbyint* if we can ignore math exceptions.  */
          if (flag_trapping_math)
            break;
-         /* ... Fall through ...  */
+         gcc_fallthrough ();
        CASE_FLT_FN (BUILT_IN_RINT):
          /* Only convert in ISO C99 mode and with -fno-math-errno.  */
          if (!targetm.libc_has_function (function_c99_misc) || flag_errno_math)
index cd5f936e3628f4708cd72408989cba10f818312a..8f12833f34fd8c94c4f4987ecc07b010fee78a99 100644 (file)
@@ -1,3 +1,33 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * parser.c (cp_parser_storage_class_specifier_opt): Add
+       gcc_fallthrough.
+       (cp_parser_skip_to_end_of_template_parameter_list): Likewise.
+       (cp_parser_cache_defarg): Likewise.
+       (cp_parser_omp_for_cond): Likewise.
+       * semantics.c (finish_decltype_type): Likewise.
+       * typeck.c (structural_comptypes): Likewise.
+       (cp_build_binary_op): Likewise.
+       (cp_build_modify_expr): Likewise.
+
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * 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_expression_statement): Handle attribute
+       fallthrough.
+       (cp_parser_statement): Likewise.
+       (cp_parser_label_for_labeled_statement): Set FALLTHROUGH_LABEL_P on
+       labels.
+       (cp_parser_std_attribute): Handle fallthrough attribute.
+       (cp_parser_check_std_attribute): Add %< %> quotes.
+       * pt.c (tsubst_copy_and_build): Handle internal functions.
+       (instantiation_dependent_scope_ref_p): Return if the expression is
+       null.
+
 2016-09-24  Marek Polacek  <polacek@redhat.com>
 
        PR c/77490
index c5dde151c2e5f16f0b9822caf99957d975738e27..bd4068e84c588b018b41f0b31ef577365a1125cd 100644 (file)
@@ -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:
@@ -4826,6 +4827,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:
index 311d0257125b1420134e9c9db3304dddb8a3567b..b4d85c908c63d34df7f9fb03d8f7305e0ae83064 100644 (file)
@@ -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);
index 168486c741c7a37869eb336b9b7fded40eec3028..5ec8b1b6ce12690dadbf185e810381d6df993bbf 100644 (file)
@@ -10585,14 +10585,31 @@ 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 (attribute_fallthrough_p (std_attrs))
+       {
+         /* The next token after the fallthrough attribute is ';'.  */
+         if (statement == NULL_TREE)
+           {
+             /* Turn [[fallthrough]]; into FALLTHROUGH ();.  */
+             statement = build_call_expr_internal_loc (statement_location,
+                                                       IFN_FALLTHROUGH,
+                                                       void_type_node, 0);
+             finish_expr_stmt (statement);
+           }
+         else
+           warning_at (statement_location, OPT_Wattributes,
+                       "%<fallthrough%> attribute not followed by %<;%>");
+         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 +10645,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 +10680,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 +10697,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 +10709,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;
     }
 
@@ -10728,6 +10759,10 @@ cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr)
 {
   tree statement = NULL_TREE;
   cp_token *token = cp_lexer_peek_token (parser->lexer);
+  location_t loc = token->location;
+
+  /* There might be attribute fallthrough.  */
+  tree attr = cp_parser_gnu_attributes_opt (parser);
 
   /* If the next token is a ';', then there is no expression
      statement.  */
@@ -10742,6 +10777,25 @@ cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr)
        }
     }
 
+  /* Handle [[fallthrough]];.  */
+  if (attribute_fallthrough_p (attr))
+    {
+      /* The next token after the fallthrough attribute is ';'.  */
+      if (statement == NULL_TREE)
+       /* Turn [[fallthrough]]; into FALLTHROUGH ();.  */
+       statement = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH,
+                                                 void_type_node, 0);
+      else
+       warning_at (loc, OPT_Wattributes,
+                   "%<fallthrough%> attribute not followed by %<;%>");
+      attr = NULL_TREE;
+    }
+
+  /* Allow "[[fallthrough]];", but warn otherwise.  */
+  if (attr != NULL_TREE)
+    warning_at (loc, OPT_Wattributes,
+               "attributes at the beginning of statement are ignored");
+
   /* Give a helpful message for "A<T>::type t;" and the like.  */
   if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)
       && !cp_parser_uncommitted_to_tentative_parse_p (parser))
@@ -12980,6 +13034,7 @@ cp_parser_storage_class_specifier_opt (cp_parser* parser)
       if (cxx_dialect != cxx98)
         return NULL_TREE;
       /* Fall through for C++98.  */
+      gcc_fallthrough ();
 
     case RID_REGISTER:
     case RID_STATIC:
@@ -24116,7 +24171,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,
@@ -24124,6 +24179,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))
@@ -24182,11 +24246,11 @@ 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");
     }
 }
@@ -27303,6 +27367,7 @@ cp_parser_skip_to_end_of_template_parameter_list (cp_parser* parser)
            }
           /* Fall through for C++0x, so we handle the second `>' in
              the `>>'.  */
+         gcc_fallthrough ();
 
        case CPP_GREATER:
          if (!nesting_depth && level-- == 0)
@@ -27760,6 +27825,7 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi)
          /* Fall through for C++0x, which treats the `>>'
             operator like two `>' tokens in certain
             cases.  */
+         gcc_fallthrough ();
 
        case CPP_GREATER:
          if (depth == 0)
@@ -33402,6 +33468,7 @@ cp_parser_omp_for_cond (cp_parser *parser, tree decl, enum tree_code code)
       if (code == CILK_SIMD || code == CILK_FOR)
        break;
       /* Fall through: OpenMP disallows NE_EXPR.  */
+      gcc_fallthrough ();
     default:
       return error_mark_node;
     }
index 29d8beb33dd2aff38b68c651d79cd7490ebed08c..a0cbb2e8c0ea1492a6d3dcf45070fcb3b838d6a2 100644 (file)
@@ -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.  */
index e4157325a36c9581c254321e7cb86e964df01e42..1d8f3369fc1b18deb106e94e7367c061679cbdb1 100644 (file)
@@ -8894,6 +8894,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
               break;
             }
           /* Fall through for fields that aren't bitfields.  */
+         gcc_fallthrough ();
 
         case FUNCTION_DECL:
         case VAR_DECL:
index 0142d4a55cfee12db144e888f03d3c04d138b74a..617ca55ffe76f2099aa0e3d2ba88f9104a88a248 100644 (file)
@@ -1306,6 +1306,7 @@ structural_comptypes (tree t1, tree t2, int strict)
       if (TYPE_REF_IS_RVALUE (t1) != TYPE_REF_IS_RVALUE (t2))
        return false;
       /* fall through to checks for pointer types */
+      gcc_fallthrough ();
 
     case POINTER_TYPE:
       if (TYPE_MODE (t1) != TYPE_MODE (t2)
@@ -4265,6 +4266,7 @@ cp_build_binary_op (location_t location,
        }
       /* The pointer - int case is just like pointer + int; fall
         through.  */
+      gcc_fallthrough ();
     case PLUS_EXPR:
       if ((code0 == POINTER_TYPE || code1 == POINTER_TYPE)
          && (code0 == INTEGER_TYPE || code1 == INTEGER_TYPE))
@@ -7554,7 +7556,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
                            TREE_OPERAND (lhs, 1)),
                    TREE_OPERAND (lhs, 0),
                    TREE_OPERAND (lhs, 1));
-      /* Fall through.  */
+      gcc_fallthrough ();
 
       /* Handle (a ? b : c) used as an "lvalue".  */
     case COND_EXPR:
index 96fed1502dc6c595a06a3d159984167939aa7b57..fcf86d320fe45d07c1f8fa4b3f33b57af09cc142 100644 (file)
@@ -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
@@ -5558,8 +5560,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}).
 
@@ -6340,7 +6342,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
@@ -6850,7 +6853,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
@@ -6903,8 +6907,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:
@@ -6935,6 +6939,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
@@ -6962,6 +7006,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}
@@ -7027,6 +7073,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},
index a5481b5d8f97bf7773123b4ad1ad9338adb97541..ce0eaef3c908ce4aad636a7e41cf0e2715878e56 100644 (file)
@@ -273,7 +273,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
+-Wimplicit  -Wimplicit-fallthrough  -Wimplicit-function-declaration @gol
+-Wimplicit-int @gol
 -Winit-self  -Winline  -Wno-int-conversion  -Wint-in-bool-context @gol
 -Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol
 -Winvalid-pch -Wlarger-than=@var{len} @gol
@@ -3719,6 +3720,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
@@ -4087,6 +4089,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
index eccc3d817101936b84c62504ceda0bc1dec8eeb5..29c12fddc67cb643aab55895da55213fe33d3c86 100644 (file)
@@ -2096,9 +2096,11 @@ output_alternate_entry_point (FILE *file, rtx_insn *insn)
     case LABEL_WEAK_ENTRY:
 #ifdef ASM_WEAKEN_LABEL
       ASM_WEAKEN_LABEL (file, name);
+      gcc_fallthrough ();
 #endif
     case LABEL_GLOBAL_ENTRY:
       targetm.asm_out.globalize_label (file, name);
+      gcc_fallthrough ();
     case LABEL_STATIC_ENTRY:
 #ifdef ASM_OUTPUT_TYPE_DIRECTIVE
       ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function");
index 5675e03039e351df805b002e2566b06941c69076..dee19ded2411765a8fb987696bb42aad18292ead 100644 (file)
@@ -1,3 +1,15 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * arith.c (eval_intrinsic): Add gcc_fallthrough.
+       * frontend-passes.c (optimize_op): Likewise.
+       (gfc_expr_walker): Likewise.
+       * parse.c (next_fixed): Likewise.
+       * primary.c (match_variable): Likewise.
+       * trans-array.c: Likewise.
+       * trans-expr.c (flatten_array_ctors_without_strlen): Likewise.
+       * trans-io.c (transfer_expr): Likewise.
+
 2016-09-25  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/77429 
index 47a5504699f3656e1ade498c57ad66e4429714a6..8af75400d80db7eef586e1b516fdd0b7b33f21a9 100644 (file)
@@ -1521,7 +1521,7 @@ eval_intrinsic (gfc_intrinsic_op op,
          break;
        }
 
-    /* Fall through  */
+    gcc_fallthrough ();
     /* Numeric binary  */
     case INTRINSIC_PLUS:
     case INTRINSIC_MINUS:
index 3a2c16e522695af7e20357eadf6ac379e4721ca8..e049fb9f2ced5e16eb472935c1f49a5844f03b5e 100644 (file)
@@ -1481,7 +1481,7 @@ optimize_op (gfc_expr *e)
     case INTRINSIC_LT:
       changed = optimize_comparison (e, op);
 
-      /* Fall through */
+      gcc_fallthrough ();
       /* Look at array constructors.  */
     case INTRINSIC_PLUS:
     case INTRINSIC_MINUS:
@@ -3349,6 +3349,7 @@ gfc_expr_walker (gfc_expr **e, walk_expr_fn_t exprfn, void *data)
 
            /* Fall through to the variable case in order to walk the
               reference.  */
+           gcc_fallthrough ();
 
          case EXPR_SUBSTRING:
          case EXPR_VARIABLE:
index a89e834b7be44fe492d5b90433f5be7de68c96d8..73cb0dbdaf965e888eb4cac750f691a6fa9e50d5 100644 (file)
@@ -1261,7 +1261,7 @@ next_fixed (void)
                  return decode_oacc_directive ();
                }
            }
-         /* FALLTHROUGH */
+         gcc_fallthrough ();
 
          /* Comments have already been skipped by the time we get
             here so don't bother checking for them.  */
index c5e9778b1f2dc0cc3b9f6735d63e5634d9b880be..85589eedc36cbab36faddf856ce2d4ee807e2c88 100644 (file)
@@ -3572,6 +3572,7 @@ match_variable (gfc_expr **result, int equiv_flag, int host_flag)
        break;
 
       /* Fall through to error */
+      gcc_fallthrough ();
 
     default:
       gfc_error ("%qs at %C is not a variable", sym->name);
index bb33a231f4acf2a63464e0123e57621cf95cbbdf..0b9776009400baa4cfc637f20b1816eb39dd2db3 100644 (file)
@@ -4032,6 +4032,7 @@ done:
                    continue;
                  }
                  /* Otherwise fall through GFC_SS_FUNCTION.  */
+                 gcc_fallthrough ();
              }
            case GFC_ISYM_LCOBOUND:
            case GFC_ISYM_UCOBOUND:
index 9fcd6a169beceeb7046e2da593b83fbba8fc1d0e..a82178835ac112d6dc7b28cbaf384cdda84e1f07 100644 (file)
@@ -2208,6 +2208,7 @@ flatten_array_ctors_without_strlen (gfc_expr* e)
        }
 
       /* Otherwise, fall through to handle constructor elements.  */
+      gcc_fallthrough ();
     case EXPR_STRUCTURE:
       for (c = gfc_constructor_first (e->value.constructor);
           c; c = gfc_constructor_next (c))
index c0559f3623720f6f138330c1bdd9bdf2456a94b1..3cdbf1fd2cac79c2c1e12dc74fa88cc41b54107b 100644 (file)
@@ -2384,6 +2384,7 @@ transfer_expr (gfc_se * se, gfc_typespec * ts, tree addr_expr,
            }
          /* If a CLASS object gets through to here, fall through and ICE.  */
        }
+      gcc_fallthrough ();
     default:
       gfc_internal_error ("Bad IO basetype (%d)", ts->type);
     }
index 1668e71c5eec57cebfd37ef1fee92a00601988e8..c8e166e3c4e754947b39fa306c03439df647b588 100644 (file)
@@ -1219,6 +1219,7 @@ make_canonical (file_location loc, struct attr_desc *attr, rtx exp)
 
       exp = newexp;
       /* Fall through to COND case since this is now a COND.  */
+      gcc_fallthrough ();
 
     case COND:
       {
@@ -3615,6 +3616,7 @@ write_test_expr (FILE *outf, rtx exp, unsigned int attrs_cached, int flags,
        }
 
       /* Otherwise, fall through to normal unary operator.  */
+      gcc_fallthrough ();
 
     /* Unary operators.  */
     case ABS:  case NEG:
index 96f75bd49f26a7e9cf6d6417655b5cc1064271cc..d18ebd2ab5a487478a95d2e15cd70ff6ca85b618 100644 (file)
@@ -74,7 +74,7 @@ validate_exp (rtx exp, const char *name, file_location loc)
              }
          }
       }
-      /* fall through */
+      gcc_fallthrough ();
 
       /* These need no special checking.  */
     case MATCH_OPERAND:
index 68115ee7896c438a04afc2e44c34dd497129ffec..b49637f5c790d61f927a9f5e15548e203347f26e 100644 (file)
@@ -1690,7 +1690,7 @@ find_candidates_dom_walker::before_dom_children (basic_block bb)
            case POINTER_PLUS_EXPR:
            case MINUS_EXPR:
              rhs2 = gimple_assign_rhs2 (gs);
-             /* Fall-through.  */
+             gcc_fallthrough ();
 
            CASE_CONVERT:
            case MODIFY_EXPR:
index 980bdf8314c4481e979178d5a23688dd31d5d966..9fad15bf3f81738a355b657db5c3ec3255166710 100644 (file)
@@ -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.  */
 
index e378ed0d1cbb0e6ef6cd20476d48c801f3254cad..66bb8be264b45bab8477233bc7b95f83eeb75a08 100644 (file)
@@ -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);
+}
+
 \f
 /* 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:
index e3448a17ee100c44ff5b52c92a73281c8e00965d..608542c63d9b721e763d3b5e5494d7cff49c2564 100644 (file)
@@ -893,6 +893,7 @@ go_format_type (struct godump_container *container, tree type,
     case UNION_TYPE:
       is_union = true;
       /* Fall through to RECORD_TYPE case.  */
+      gcc_fallthrough ();
     case RECORD_TYPE:
       {
        unsigned int prev_field_end;
index c269ca6b940cfda11c6be9b01e4c84fdaa70d512..029a534e9eb838c78f6b184f6ba4299ac5a20328 100644 (file)
@@ -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
index 6701cd95afbe70597ccc1b975e8dd0587e70f0a1..d4fbdb286220797464d8b7e580da403d73474cb7 100644 (file)
@@ -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
index 538d9f9271084d9b1f407a21dabb145ee73fde89..79a846cd432a4c107e59a49ae9618e4b0478d58a 100644 (file)
@@ -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;
+}
index c109c8c7e150c48e385d585ded155fd54d294f25..cfaee621c6172d10ecbe53e5f7a4f5b122d40645 100644 (file)
@@ -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 */
index da53cfa5f95bce15057cdfbe998b2f85aba690ac..55aafe46e877c8252ec519b74d9b7f5fe245a82a 100644 (file)
@@ -3022,6 +3022,7 @@ elimination_effects (rtx x, machine_mode mem_mode)
        break;
 
       /* Fall through to generic unary operation case.  */
+      gcc_fallthrough ();
     case STRICT_LOW_PART:
     case NEG:          case NOT:
     case SIGN_EXTEND:  case ZERO_EXTEND:
index 1d7ce958667e294ef3ec03ac92ee9f45e6d2d86f..32e3e44856dcc0a11c1ed6a1a17ad77127af5399 100644 (file)
@@ -364,6 +364,7 @@ mark_referenced_resources (rtx x, struct resources *res,
        }
 
       /* ... fall through to other INSN processing ...  */
+      gcc_fallthrough ();
 
     case INSN:
     case JUMP_INSN:
@@ -674,6 +675,7 @@ mark_set_resources (rtx x, struct resources *res, int in_dest,
        }
 
       /* ... and also what its RTL says it modifies, if anything.  */
+      gcc_fallthrough ();
 
     case JUMP_INSN:
     case INSN:
index 8a1719748196bc05443b6c9d1ef8a6c3373d7509..8ca71cf0f0e4b15c723aaeec0f45c44ac3a85674 100644 (file)
@@ -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
index 45e5307f679f15947be343bb66037d669085c540..2dd006e194065c07b144784abc1c824d1a6fdbe9 100644 (file)
@@ -1,3 +1,37 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * 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.
+       * g++.dg/warn/Wunused-label-1.C: Turn dg-error into dg-warning.
+       * 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.
+
 2016-09-25  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/77429 
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c
new file mode 100644 (file)
index 0000000..b45880f
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
new file mode 100644 (file)
index 0000000..99e44d9
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
new file mode 100644 (file)
index 0000000..e8f47f5
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
new file mode 100644 (file)
index 0000000..91a68ab
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
new file mode 100644 (file)
index 0000000..f3ec79f
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
new file mode 100644 (file)
index 0000000..b7c825b
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
new file mode 100644 (file)
index 0000000..ee3e52d
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
new file mode 100644 (file)
index 0000000..923f012
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
new file mode 100644 (file)
index 0000000..23ff5f1
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
new file mode 100644 (file)
index 0000000..2c8a3cb
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
new file mode 100644 (file)
index 0000000..b7a3791
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
new file mode 100644 (file)
index 0000000..4dfb278
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
new file mode 100644 (file)
index 0000000..d37a840
--- /dev/null
@@ -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-warning "ignored|only attribute" } */
+    default:
+      __attribute__((used)); /* { dg-warning "ignored|only attribute" } */
+    case 1:
+      return 6;
+    case 2 ... 4:
+      __attribute__((used)); /* { dg-warning "ignored|only attribute" } */
+    case 5:
+      return 7;
+    }
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c
new file mode 100644 (file)
index 0000000..6092a90
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
new file mode 100644 (file)
index 0000000..fbb9712
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
new file mode 100644 (file)
index 0000000..9a0aeb7
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
new file mode 100644 (file)
index 0000000..9317484
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
new file mode 100644 (file)
index 0000000..8364c1b
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
new file mode 100644 (file)
index 0000000..21a158c
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
new file mode 100644 (file)
index 0000000..0ed7928
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
new file mode 100644 (file)
index 0000000..394d699
--- /dev/null
@@ -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 a/gcc/testsuite/c-c++-common/attr-fallthrough-1.c b/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
new file mode 100644 (file)
index 0000000..ecfd094
--- /dev/null
@@ -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 "ignored|attribute not followed" } */
+
+  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 a/gcc/testsuite/c-c++-common/attr-fallthrough-2.c b/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
new file mode 100644 (file)
index 0000000..959564b
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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-warning "ignored" "" { target c++ } 13 } */
+  case 2:
+    bar (1);
+    __attribute__((foo));
+    /* { dg-warning "empty declaration" "" { target c } 18 } */
+    /* { dg-warning "ignored" "" { target c++ } 18 } */
+  case 3:
+    bar (1);
+    __attribute__((fallthrough)) /* { dg-warning "not followed" "" { target c } } */
+  case 4: /* { dg-error "expected" } */
+    bar (1);
+    __attribute__((fallthrough)) 1;
+    /* { dg-error "expected" "" { target c } 26 } */
+    /* { dg-warning "not followed" "" { target *-*-* } 26 } */
+  case 5:
+    bar (1);
+    __attribute__((fallthrough)) int i; /* { dg-warning "ignored|not followed" } */
+  case 6:
+    bar (1);
+    __attribute__((fallthrough ("x"))); /* { dg-warning "specified with a parameter" } */
+  case 7:
+    bar (1);
+    __attribute__((fallthrough, fallthrough)); /* { dg-warning "attribute specified multiple times" } */
+  case 8:
+    bar (1);
+    __attribute__((fallthrough));
+  case 9:
+    __attribute__((fallthrough));
+    /* { dg-warning "not preceding" "" { target *-*-* } 42 } */
+    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 a/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C b/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C
new file mode 100644 (file)
index 0000000..523067e
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C b/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
new file mode 100644 (file)
index 0000000..b6964e1
--- /dev/null
@@ -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-warning ".fallthrough. attribute specified multiple times" }
+    case 2:
+      bar (2);
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C b/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C
new file mode 100644 (file)
index 0000000..d15b1ea
--- /dev/null
@@ -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);
+    }
+}
index 96f49b321cd88c9b4904989f7972273fb0fc1768..255a26e0e5a8e39c8a3d297fc31ee9579cc92c04 100644 (file)
@@ -21,7 +21,7 @@ void
 f3()
 {
   // The next line would be OK in C but is a syntax error in C++.
- l2: __attribute__ ((unused)) f9();    // { dg-error "expected" }
+ l2: __attribute__ ((unused)) f9();    // { dg-warning "ignored" }
                // We still get an unused label warning--this is
                // optional and can be removed if it ever changes.
                // { dg-warning "not used" "expected" { target *-*-* } 24 }
diff --git a/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c b/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c
new file mode 100644 (file)
index 0000000..f8b54f5
--- /dev/null
@@ -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 a/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm b/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
new file mode 100644 (file)
index 0000000..b45880f
--- /dev/null
@@ -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 a/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m b/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
new file mode 100644 (file)
index 0000000..b45880f
--- /dev/null
@@ -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);
+    }
+}
index 8b3e5cc75a4e2cb80c0078558f8ddaa94bd1dc96..353a62504250451523a5298bf47f1694a304d1ed 100644 (file)
@@ -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
index 6886790a4c5b86b00f3205863f434c5b048dc8df..7f78734ea229fda7c46751e9ba7ac281d8730bc7 100644 (file)
@@ -1885,8 +1885,8 @@ find_deriving_biv_for_expr (struct ivopts_data *data, tree expr)
       iv = find_deriving_biv_for_expr (data, e2);
       if (iv)
        return iv;
+      gcc_fallthrough ();
 
-      /* Fallthru.  */
     CASE_CONVERT:
       /* Casts are simple.  */
       return find_deriving_biv_for_expr (data, e1);
index 38ee81675757902f147ac3d4e068bbdb56f925c9..0d9ad0198faadb471b798f622ba24916c5e02c6b 100644 (file)
@@ -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.
index 4da9e21b69ce8c0251bda6b882dfd999b313b0ff..3972790cc7bdfd72e91c2c59d678e2187c70d5c7 100644 (file)
@@ -4173,7 +4173,7 @@ output_addressed_constants (tree exp)
     case POINTER_PLUS_EXPR:
     case MINUS_EXPR:
       output_addressed_constants (TREE_OPERAND (exp, 1));
-      /* Fall through.  */
+      gcc_fallthrough ();
 
     CASE_CONVERT:
     case VIEW_CONVERT_EXPR:
index 61304cb6bcbf0274aae29d2d410c29a70a869c81..94ea99db8a129ff9c7afadc9c0e5f0fd57987418 100644 (file)
@@ -1,3 +1,13 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c/7652
+       * 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.
+
 2016-09-23  David Malcolm  <dmalcolm@redhat.com>
 
        PR preprocessor/77672
index cfc6ccd1a907c5067c02bf0517a8a00884ae3bc7..6352ac586d4c14d9080450edaa0c72afc9fe0ee3 100644 (file)
@@ -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.  */
 
index 6254ed69ca20fbbc9d2f717ef2941b87f871e629..0c47e2980701ace96e576ffdb05cce50e4cd3977 100644 (file)
@@ -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;
index 6f134cbe2e2b9fda667c00a29e19eca649fef1ef..230f764fd6f4d22d66851b7e62fd8463afe05e64 100644 (file)
@@ -1,3 +1,8 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+
+       PR c/7652
+       * libsupc++/hash_bytes.cc: Add [[gnu::fallthrough]].
+
 2016-09-25  François Dumont  <fdumont@gcc.gnu.org>
 
        * src/c++11/debug.cc: Include debug/vector. Include cctype. Remove
index 2e5bbfaca1968d13fff3ece26bbdabc5096baa55..1042de66388a46cf85bc5f8ec736ad175542d0a3 100644 (file)
@@ -95,8 +95,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
       case 3:
        hash ^= static_cast<unsigned char>(buf[2]) << 16;
+       [[gnu::fallthrough]];
       case 2:
        hash ^= static_cast<unsigned char>(buf[1]) << 8;
+       [[gnu::fallthrough]];
       case 1:
        hash ^= static_cast<unsigned char>(buf[0]);
        hash *= m;