aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog56
-rw-r--r--gcc/Makefile.in5
-rw-r--r--gcc/builtins.c5
-rw-r--r--gcc/c-family/ChangeLog8
-rw-r--r--gcc/c-family/c-common.c42
-rw-r--r--gcc/c-family/c-common.h1
-rw-r--r--gcc/c/ChangeLog16
-rw-r--r--gcc/c/c-decl.c2
-rw-r--r--gcc/c/c-parser.c97
-rw-r--r--gcc/common.opt4
-rw-r--r--gcc/config/rs6000/rs6000.c2
-rw-r--r--gcc/convert.c4
-rw-r--r--gcc/cp/ChangeLog30
-rw-r--r--gcc/cp/constexpr.c2
-rw-r--r--gcc/cp/constraint.cc3
-rw-r--r--gcc/cp/parser.c81
-rw-r--r--gcc/cp/pt.c13
-rw-r--r--gcc/cp/semantics.c1
-rw-r--r--gcc/cp/typeck.c4
-rw-r--r--gcc/doc/extend.texi62
-rw-r--r--gcc/doc/invoke.texi91
-rw-r--r--gcc/final.c2
-rw-r--r--gcc/fortran/ChangeLog12
-rw-r--r--gcc/fortran/arith.c2
-rw-r--r--gcc/fortran/frontend-passes.c3
-rw-r--r--gcc/fortran/parse.c2
-rw-r--r--gcc/fortran/primary.c1
-rw-r--r--gcc/fortran/trans-array.c1
-rw-r--r--gcc/fortran/trans-expr.c1
-rw-r--r--gcc/fortran/trans-io.c1
-rw-r--r--gcc/genattrtab.c2
-rw-r--r--gcc/genpreds.c2
-rw-r--r--gcc/gimple-ssa-strength-reduction.c2
-rw-r--r--gcc/gimple.h10
-rw-r--r--gcc/gimplify.c454
-rw-r--r--gcc/godump.c1
-rw-r--r--gcc/internal-fn.c9
-rw-r--r--gcc/internal-fn.def3
-rw-r--r--gcc/langhooks.c9
-rw-r--r--gcc/langhooks.h3
-rw-r--r--gcc/reload1.c1
-rw-r--r--gcc/resource.c2
-rw-r--r--gcc/system.h6
-rw-r--r--gcc/testsuite/ChangeLog34
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c38
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c239
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c23
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c26
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c63
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c162
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c31
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c32
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c29
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c42
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c85
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c223
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c41
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c25
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c543
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c250
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c109
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c305
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c124
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c101
-rw-r--r--gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c26
-rw-r--r--gcc/testsuite/c-c++-common/attr-fallthrough-1.c57
-rw-r--r--gcc/testsuite/c-c++-common/attr-fallthrough-2.c54
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/fallthrough1.C57
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/fallthrough2.C21
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/fallthrough1.C20
-rw-r--r--gcc/testsuite/g++.dg/warn/Wunused-label-1.C2
-rw-r--r--gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c22
-rw-r--r--gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm38
-rw-r--r--gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m38
-rw-r--r--gcc/tree-core.h3
-rw-r--r--gcc/tree-ssa-loop-ivopts.c2
-rw-r--r--gcc/tree.h5
-rw-r--r--gcc/varasm.c2
-rw-r--r--libcpp/ChangeLog10
-rw-r--r--libcpp/include/cpplib.h4
-rw-r--r--libcpp/lex.c100
-rw-r--r--libstdc++-v3/ChangeLog5
-rw-r--r--libstdc++-v3/libsupc++/hash_bytes.cc2
83 files changed, 4005 insertions, 46 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 8481198..d30a7cf 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 69ff9fa..e8559cb 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -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
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 9a19a75..93cbe15 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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:
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 8bcbd06..cd3eeab 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 48d8b05..e9f619f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -396,6 +396,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *);
static tree handle_bnd_legacy (tree *, tree, tree, int, bool *);
static tree handle_bnd_instrument (tree *, tree, tree, int, bool *);
+static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT);
@@ -847,6 +848,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_bnd_legacy, false },
{ "bnd_instrument", 0, 0, true, false, false,
handle_bnd_instrument, false },
+ { "fallthrough", 0, 0, false, false, false,
+ handle_fallthrough_attribute, false },
{ NULL, 0, 0, false, false, false, NULL, false }
};
@@ -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;
+}
+
/* Check for valid arguments being passed to a function with FNTYPE.
There are NARGS arguments in the array ARGARRAY. LOC should be used for
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 5bbf951..c88619b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -805,6 +805,7 @@ extern void check_function_arguments_recurse (void (*)
extern bool check_builtin_function_arguments (location_t, vec<location_t>,
tree, int, tree *);
extern void check_function_format (tree, int, tree *);
+extern bool attribute_fallthrough_p (tree);
extern tree handle_unused_attribute (tree *, tree, tree, int, bool *);
extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index cae5c92..642c20c 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -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
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index d15b8f8..9e32be2 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -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.
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 5f610e9..6bc42da 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -193,6 +193,8 @@ struct GTY (()) c_token {
location_t location;
/* The value associated with this token, if any. */
tree value;
+ /* Token flags. */
+ unsigned char flags;
source_range get_range () const
{
@@ -270,7 +272,8 @@ c_lex_one_token (c_parser *parser, c_token *token)
{
timevar_push (TV_LEX);
- token->type = c_lex_with_flags (&token->value, &token->location, NULL,
+ token->type = c_lex_with_flags (&token->value, &token->location,
+ &token->flags,
(parser->lex_untranslated_string
? C_LEX_STRING_NO_TRANSLATE : 0));
token->id_kind = C_ID_NONE;
@@ -1288,7 +1291,8 @@ static void c_parser_external_declaration (c_parser *);
static void c_parser_asm_definition (c_parser *);
static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool,
bool, bool, tree *, vec<c_token>,
- struct oacc_routine_data * = NULL);
+ struct oacc_routine_data * = NULL,
+ bool * = NULL);
static void c_parser_static_assert_declaration_no_semi (c_parser *);
static void c_parser_static_assert_declaration (c_parser *);
static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
@@ -1591,6 +1595,8 @@ static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
attributes; otherwise they may not.
OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed
declaration when parsing an Objective-C foreach statement.
+ FALLTHRU_ATTR_P is used to signal whether this function parsed
+ "__attribute__((fallthrough));".
declaration:
declaration-specifiers init-declarator-list[opt] ;
@@ -1618,6 +1624,8 @@ static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
declaration-specifiers declarator declaration-list[opt]
compound-statement
+ attribute ;
+
Objective-C:
attributes objc-class-definition
attributes objc-category-definition
@@ -1652,7 +1660,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
bool nested, bool start_attr_ok,
tree *objc_foreach_object_declaration,
vec<c_token> omp_declare_simd_clauses,
- struct oacc_routine_data *oacc_routine_data)
+ struct oacc_routine_data *oacc_routine_data,
+ bool *fallthru_attr_p)
{
struct c_declspecs *specs;
tree prefix_attrs;
@@ -1749,6 +1758,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;
}
diff --git a/gcc/common.opt b/gcc/common.opt
index 8c0885c..b1d32fb 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -601,6 +601,10 @@ Whsa
Common Var(warn_hsa) Init(1) Warning
Warn when a function cannot be expanded to HSAIL.
+Wimplicit-fallthrough
+Common Var(warn_implicit_fallthrough) Warning EnabledBy(Wextra)
+Warn when a switch case falls through.
+
Winline
Common Var(warn_inline) Warning
Warn when an inlined function cannot be inlined.
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 5d189fc..d76f479 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -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:
diff --git a/gcc/convert.c b/gcc/convert.c
index e6b4d29..8f18ee4 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -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)
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index cd5f936..8f12833 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index c5dde15..bd4068e 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1303,6 +1303,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
+ case IFN_FALLTHROUGH:
return void_node;
case IFN_ADD_OVERFLOW:
@@ -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:
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 311d025..b4d85c9 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -116,7 +116,8 @@ function_concept_check_p (tree t)
{
gcc_assert (TREE_CODE (t) == CALL_EXPR);
tree fn = CALL_EXPR_FN (t);
- if (TREE_CODE (fn) == TEMPLATE_ID_EXPR
+ if (fn != NULL_TREE
+ && TREE_CODE (fn) == TEMPLATE_ID_EXPR
&& TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD)
{
tree f1 = get_first_fn (fn);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 168486c..5ec8b1b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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;
}
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 29d8beb..a0cbb2e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -16556,7 +16556,16 @@ tsubst_copy_and_build (tree t,
tree ret;
function = CALL_EXPR_FN (t);
- /* When we parsed the expression, we determined whether or
+ if (function == NULL_TREE)
+ {
+ /* If you hit this assert, it means that you're trying to tsubst
+ an internal function with arguments. This isn't yet supported,
+ so you need to build another internal call with the tsubsted
+ arguments after the arguments have been tsubsted down below. */
+ gcc_assert (call_expr_nargs (t) == 0);
+ RETURN (t);
+ }
+ /* When we parsed the expression, we determined whether or
not Koenig lookup should be performed. */
koenig_p = KOENIG_LOOKUP_P (t);
if (TREE_CODE (function) == SCOPE_REF)
@@ -22787,7 +22796,7 @@ instantiation_dependent_scope_ref_p (tree t)
bool
value_dependent_expression_p (tree expression)
{
- if (!processing_template_decl)
+ if (!processing_template_decl || expression == NULL_TREE)
return false;
/* A name declared with a dependent type. */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e415732..1d8f336 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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:
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 0142d4a..617ca55 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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:
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 96fed15..fcf86d3 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -60,6 +60,7 @@ extensions, accepted by GCC in C90 mode and in C++.
* Type Attributes:: Specifying attributes of types.
* Label Attributes:: Specifying attributes on labels.
* Enumerator Attributes:: Specifying attributes on enumerators.
+* Statement Attributes:: Specifying attributes on statements.
* Attribute Syntax:: Formal syntax for attributes.
* Function Prototypes:: Prototype declarations and old-style definitions.
* C++ Comments:: C++ comments are recognized.
@@ -2261,6 +2262,7 @@ GCC also supports attributes on
variable declarations (@pxref{Variable Attributes}),
labels (@pxref{Label Attributes}),
enumerators (@pxref{Enumerator Attributes}),
+statements (@pxref{Statement Attributes}),
and types (@pxref{Type Attributes}).
There is some overlap between the purposes of attributes and pragmas
@@ -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},
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a5481b5..ce0eaef 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/final.c b/gcc/final.c
index eccc3d8..29c12fd 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -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");
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index 5675e03..dee19de 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -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
diff --git a/gcc/fortran/arith.c b/gcc/fortran/arith.c
index 47a5504..8af7540 100644
--- a/gcc/fortran/arith.c
+++ b/gcc/fortran/arith.c
@@ -1521,7 +1521,7 @@ eval_intrinsic (gfc_intrinsic_op op,
break;
}
- /* Fall through */
+ gcc_fallthrough ();
/* Numeric binary */
case INTRINSIC_PLUS:
case INTRINSIC_MINUS:
diff --git a/gcc/fortran/frontend-passes.c b/gcc/fortran/frontend-passes.c
index 3a2c16e..e049fb9 100644
--- a/gcc/fortran/frontend-passes.c
+++ b/gcc/fortran/frontend-passes.c
@@ -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:
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index a89e834..73cb0db 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -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. */
diff --git a/gcc/fortran/primary.c b/gcc/fortran/primary.c
index c5e9778..85589ee 100644
--- a/gcc/fortran/primary.c
+++ b/gcc/fortran/primary.c
@@ -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);
diff --git a/gcc/fortran/trans-array.c b/gcc/fortran/trans-array.c
index bb33a23..0b97760 100644
--- a/gcc/fortran/trans-array.c
+++ b/gcc/fortran/trans-array.c
@@ -4032,6 +4032,7 @@ done:
continue;
}
/* Otherwise fall through GFC_SS_FUNCTION. */
+ gcc_fallthrough ();
}
case GFC_ISYM_LCOBOUND:
case GFC_ISYM_UCOBOUND:
diff --git a/gcc/fortran/trans-expr.c b/gcc/fortran/trans-expr.c
index 9fcd6a1..a821788 100644
--- a/gcc/fortran/trans-expr.c
+++ b/gcc/fortran/trans-expr.c
@@ -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))
diff --git a/gcc/fortran/trans-io.c b/gcc/fortran/trans-io.c
index c0559f3..3cdbf1f 100644
--- a/gcc/fortran/trans-io.c
+++ b/gcc/fortran/trans-io.c
@@ -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);
}
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 1668e71..c8e166e 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -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:
diff --git a/gcc/genpreds.c b/gcc/genpreds.c
index 96f75bd..d18ebd2 100644
--- a/gcc/genpreds.c
+++ b/gcc/genpreds.c
@@ -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:
diff --git a/gcc/gimple-ssa-strength-reduction.c b/gcc/gimple-ssa-strength-reduction.c
index 68115ee..b49637f 100644
--- a/gcc/gimple-ssa-strength-reduction.c
+++ b/gcc/gimple-ssa-strength-reduction.c
@@ -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:
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 980bdf8..9fad15b 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -2921,6 +2921,16 @@ gimple_call_internal_unique_p (const gimple *gs)
return gimple_call_internal_unique_p (gc);
}
+/* Return true if GS is an internal function FN. */
+
+static inline bool
+gimple_call_internal_p (const gimple *gs, internal_fn fn)
+{
+ return (is_gimple_call (gs)
+ && gimple_call_internal_p (gs)
+ && gimple_call_internal_fn (gs) == fn);
+}
+
/* If CTRL_ALTERING_P is true, mark GIMPLE_CALL S to be a stmt
that could alter control flow. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index e378ed0..66bb8be 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -160,6 +160,7 @@ struct gimplify_ctx
unsigned in_cleanup_point_expr : 1;
unsigned keep_stack : 1;
unsigned save_stack : 1;
+ unsigned in_switch_expr : 1;
};
struct gimplify_omp_ctx
@@ -1626,6 +1627,430 @@ maybe_warn_switch_unreachable (gimple_seq seq)
}
}
+
+/* A label entry that pairs label and a location. */
+struct label_entry
+{
+ tree label;
+ location_t loc;
+};
+
+/* Find LABEL in vector of label entries VEC. */
+
+static struct label_entry *
+find_label_entry (const auto_vec<struct label_entry> *vec, tree label)
+{
+ unsigned int i;
+ struct label_entry *l;
+
+ FOR_EACH_VEC_ELT (*vec, i, l)
+ if (l->label == label)
+ return l;
+ return NULL;
+}
+
+/* Return true if LABEL, a LABEL_DECL, represents a case label
+ in a vector of labels CASES. */
+
+static bool
+case_label_p (const vec<tree> *cases, tree label)
+{
+ unsigned int i;
+ tree l;
+
+ FOR_EACH_VEC_ELT (*cases, i, l)
+ if (CASE_LABEL (l) == label)
+ return true;
+ return false;
+}
+
+/* Find the last statement in a scope STMT. */
+
+static gimple *
+last_stmt_in_scope (gimple *stmt)
+{
+ if (!stmt)
+ return NULL;
+
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_BIND:
+ {
+ gbind *bind = as_a <gbind *> (stmt);
+ stmt = gimple_seq_last_stmt (gimple_bind_body (bind));
+ return last_stmt_in_scope (stmt);
+ }
+
+ case GIMPLE_TRY:
+ {
+ gtry *try_stmt = as_a <gtry *> (stmt);
+ stmt = gimple_seq_last_stmt (gimple_try_eval (try_stmt));
+ gimple *last_eval = last_stmt_in_scope (stmt);
+ if (gimple_stmt_may_fallthru (last_eval)
+ && gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
+ {
+ stmt = gimple_seq_last_stmt (gimple_try_cleanup (try_stmt));
+ return last_stmt_in_scope (stmt);
+ }
+ else
+ return last_eval;
+ }
+
+ default:
+ return stmt;
+ }
+}
+
+/* Collect interesting labels in LABELS and return the statement preceding
+ another case label, or a user-defined label. */
+
+static gimple *
+collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
+ auto_vec <struct label_entry> *labels)
+{
+ gimple *prev = NULL;
+
+ do
+ {
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND
+ || gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY)
+ {
+ /* Nested scope. Only look at the last statement of
+ the innermost scope. */
+ location_t bind_loc = gimple_location (gsi_stmt (*gsi_p));
+ gimple *last = last_stmt_in_scope (gsi_stmt (*gsi_p));
+ if (last)
+ {
+ prev = last;
+ /* It might be a label without a location. Use the
+ location of the scope then. */
+ if (!gimple_has_location (prev))
+ gimple_set_location (prev, bind_loc);
+ }
+ gsi_next (gsi_p);
+ continue;
+ }
+
+ /* Ifs are tricky. */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_COND)
+ {
+ gcond *cond_stmt = as_a <gcond *> (gsi_stmt (*gsi_p));
+ tree false_lab = gimple_cond_false_label (cond_stmt);
+ location_t if_loc = gimple_location (cond_stmt);
+
+ /* If we have e.g.
+ if (i > 1) goto <D.2259>; else goto D;
+ we can't do much with the else-branch. */
+ if (!DECL_ARTIFICIAL (false_lab))
+ break;
+
+ /* Go on until the false label, then one step back. */
+ for (; !gsi_end_p (*gsi_p); gsi_next (gsi_p))
+ {
+ gimple *stmt = gsi_stmt (*gsi_p);
+ if (gimple_code (stmt) == GIMPLE_LABEL
+ && gimple_label_label (as_a <glabel *> (stmt)) == false_lab)
+ break;
+ }
+
+ /* Not found? Oops. */
+ if (gsi_end_p (*gsi_p))
+ break;
+
+ struct label_entry l = { false_lab, if_loc };
+ labels->safe_push (l);
+
+ /* Go to the last statement of the then branch. */
+ gsi_prev (gsi_p);
+
+ /* if (i != 0) goto <D.1759>; else goto <D.1760>;
+ <D.1759>:
+ <stmt>;
+ goto <D.1761>;
+ <D.1760>:
+ */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+ && !gimple_has_location (gsi_stmt (*gsi_p)))
+ {
+ /* Look at the statement before, it might be
+ attribute fallthrough, in which case don't warn. */
+ gsi_prev (gsi_p);
+ bool fallthru_before_dest
+ = gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_FALLTHROUGH);
+ gsi_next (gsi_p);
+ tree goto_dest = gimple_goto_dest (gsi_stmt (*gsi_p));
+ if (!fallthru_before_dest)
+ {
+ struct label_entry l = { goto_dest, if_loc };
+ labels->safe_push (l);
+ }
+ }
+ /* And move back. */
+ gsi_next (gsi_p);
+ }
+
+ /* Remember the last statement. Skip labels that are of no interest
+ to us. */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+ {
+ tree label = gimple_label_label (as_a <glabel *> (gsi_stmt (*gsi_p)));
+ if (find_label_entry (labels, label))
+ prev = gsi_stmt (*gsi_p);
+ }
+ else
+ prev = gsi_stmt (*gsi_p);
+ gsi_next (gsi_p);
+ }
+ while (!gsi_end_p (*gsi_p)
+ /* Stop if we find a case or a user-defined label. */
+ && (gimple_code (gsi_stmt (*gsi_p)) != GIMPLE_LABEL
+ || !gimple_has_location (gsi_stmt (*gsi_p))));
+
+ return prev;
+}
+
+/* Return true if the switch fallthough warning should occur. LABEL is
+ the label statement that we're falling through to. */
+
+static bool
+should_warn_for_implicit_fallthrough (gimple_stmt_iterator *gsi_p, tree label)
+{
+ gimple_stmt_iterator gsi = *gsi_p;
+
+ /* Don't warn for a non-case label followed by a statement:
+ case 0:
+ foo ();
+ label:
+ bar ();
+ as these are likely intentional. */
+ if (!case_label_p (&gimplify_ctxp->case_labels, label))
+ {
+ gsi_next (&gsi);
+ if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
+ return false;
+ }
+
+ /* Don't warn for terminated branches, i.e. when the subsequent case labels
+ immediately breaks. */
+ gsi = *gsi_p;
+
+ /* Skip all immediately following labels. */
+ while (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+ gsi_next (&gsi);
+
+ /* { ... something; default:; } */
+ if (gsi_end_p (gsi)
+ /* { ... something; default: break; } or
+ { ... something; default: goto L; } */
+ || gimple_code (gsi_stmt (gsi)) == GIMPLE_GOTO
+ /* { ... something; default: return; } */
+ || gimple_code (gsi_stmt (gsi)) == GIMPLE_RETURN)
+ return false;
+
+ return true;
+}
+
+/* Callback for walk_gimple_seq. */
+
+static tree
+warn_implicit_fallthrough_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
+ struct walk_stmt_info *)
+{
+ gimple *stmt = gsi_stmt (*gsi_p);
+
+ *handled_ops_p = true;
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_TRY:
+ case GIMPLE_BIND:
+ case GIMPLE_CATCH:
+ case GIMPLE_EH_FILTER:
+ case GIMPLE_TRANSACTION:
+ /* Walk the sub-statements. */
+ *handled_ops_p = false;
+ break;
+
+ /* Find a sequence of form:
+
+ GIMPLE_LABEL
+ [...]
+ <may fallthru stmt>
+ GIMPLE_LABEL
+
+ and possibly warn. */
+ case GIMPLE_LABEL:
+ {
+ /* Found a label. Skip all immediately following labels. */
+ while (!gsi_end_p (*gsi_p)
+ && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+ gsi_next (gsi_p);
+
+ /* There might be no more statements. */
+ if (gsi_end_p (*gsi_p))
+ return integer_zero_node;
+
+ /* Vector of labels that fall through. */
+ auto_vec <struct label_entry> labels;
+ gimple *prev = collect_fallthrough_labels (gsi_p, &labels);
+
+ /* There might be no more statements. */
+ if (gsi_end_p (*gsi_p))
+ return integer_zero_node;
+
+ gimple *next = gsi_stmt (*gsi_p);
+ tree label;
+ /* If what follows is a label, then we may have a fallthrough. */
+ if (gimple_code (next) == GIMPLE_LABEL
+ && gimple_has_location (next)
+ && (label = gimple_label_label (as_a <glabel *> (next)))
+ && !FALLTHROUGH_LABEL_P (label)
+ && prev != NULL)
+ {
+ struct label_entry *l;
+ bool warned_p = false;
+ if (!should_warn_for_implicit_fallthrough (gsi_p, label))
+ /* Quiet. */;
+ else if (gimple_code (prev) == GIMPLE_LABEL
+ && (label = gimple_label_label (as_a <glabel *> (prev)))
+ && (l = find_label_entry (&labels, label)))
+ warned_p = warning_at (l->loc, OPT_Wimplicit_fallthrough,
+ "this statement may fall through");
+ else if (!gimple_call_internal_p (prev, IFN_FALLTHROUGH)
+ /* Try to be clever and don't warn when the statement
+ can't actually fall through. */
+ && gimple_stmt_may_fallthru (prev)
+ && gimple_has_location (prev))
+ warned_p = warning_at (gimple_location (prev),
+ OPT_Wimplicit_fallthrough,
+ "this statement may fall through");
+ if (warned_p)
+ inform (gimple_location (next), "here");
+
+ /* Mark this label as processed so as to prevent multiple
+ warnings in nested switches. */
+ FALLTHROUGH_LABEL_P (label) = true;
+
+ /* So that next warn_implicit_fallthrough_r will start looking for
+ a new sequence starting with this label. */
+ gsi_prev (gsi_p);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Warn when a switch case falls through. */
+
+static void
+maybe_warn_implicit_fallthrough (gimple_seq seq)
+{
+ if (!warn_implicit_fallthrough)
+ return;
+
+ /* This warning is meant for C/C++/ObjC/ObjC++ only. */
+ if (!(lang_GNU_C ()
+ || lang_GNU_CXX ()
+ || lang_GNU_OBJC ()))
+ return;
+
+ struct walk_stmt_info wi;
+ memset (&wi, 0, sizeof (wi));
+ walk_gimple_seq (seq, warn_implicit_fallthrough_r, NULL, &wi);
+}
+
+/* Callback for walk_gimple_seq. */
+
+static tree
+expand_FALLTHROUGH_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
+ struct walk_stmt_info *)
+{
+ gimple *stmt = gsi_stmt (*gsi_p);
+
+ *handled_ops_p = true;
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_TRY:
+ case GIMPLE_BIND:
+ case GIMPLE_CATCH:
+ case GIMPLE_EH_FILTER:
+ case GIMPLE_TRANSACTION:
+ /* Walk the sub-statements. */
+ *handled_ops_p = false;
+ break;
+ case GIMPLE_CALL:
+ if (gimple_call_internal_p (stmt, IFN_FALLTHROUGH))
+ {
+ gsi_remove (gsi_p, true);
+ if (gsi_end_p (*gsi_p))
+ return integer_zero_node;
+
+ bool found = false;
+ location_t loc = gimple_location (stmt);
+
+ gimple_stmt_iterator gsi2 = *gsi_p;
+ stmt = gsi_stmt (gsi2);
+ if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
+ {
+ /* Go on until the artificial label. */
+ tree goto_dest = gimple_goto_dest (stmt);
+ for (; !gsi_end_p (gsi2); gsi_next (&gsi2))
+ {
+ if (gimple_code (gsi_stmt (gsi2)) == GIMPLE_LABEL
+ && gimple_label_label (as_a <glabel *> (gsi_stmt (gsi2)))
+ == goto_dest)
+ break;
+ }
+
+ /* Not found? Stop. */
+ if (gsi_end_p (gsi2))
+ break;
+
+ /* Look one past it. */
+ gsi_next (&gsi2);
+ }
+
+ /* We're looking for a case label or default label here. */
+ while (!gsi_end_p (gsi2))
+ {
+ stmt = gsi_stmt (gsi2);
+ if (gimple_code (stmt) == GIMPLE_LABEL)
+ {
+ tree label = gimple_label_label (as_a <glabel *> (stmt));
+ if (gimple_has_location (stmt) && DECL_ARTIFICIAL (label))
+ {
+ found = true;
+ break;
+ }
+ }
+ else
+ /* Something other than a label. That's not expected. */
+ break;
+ gsi_next (&gsi2);
+ }
+ if (!found)
+ warning_at (loc, 0, "attribute %<fallthrough%> not preceding "
+ "a case label or default label");
+ }
+ break;
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Expand all FALLTHROUGH () calls in SEQ. */
+
+static void
+expand_FALLTHROUGH (gimple_seq *seq_p)
+{
+ struct walk_stmt_info wi;
+ memset (&wi, 0, sizeof (wi));
+ walk_gimple_seq_mod (seq_p, expand_FALLTHROUGH_r, NULL, &wi);
+}
+
/* Gimplify a SWITCH_EXPR, and collect the vector of labels it can
branch to. */
@@ -1660,10 +2085,17 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
labels. Save all the things from the switch body to append after. */
saved_labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels.create (8);
+ bool old_in_switch_expr = gimplify_ctxp->in_switch_expr;
+ gimplify_ctxp->in_switch_expr = true;
gimplify_stmt (&SWITCH_BODY (switch_expr), &switch_body_seq);
+ gimplify_ctxp->in_switch_expr = old_in_switch_expr;
maybe_warn_switch_unreachable (switch_body_seq);
+ maybe_warn_implicit_fallthrough (switch_body_seq);
+ /* Only do this for the outermost GIMPLE_SWITCH. */
+ if (!gimplify_ctxp->in_switch_expr)
+ expand_FALLTHROUGH (&switch_body_seq);
labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels = saved_labels;
@@ -1694,6 +2126,21 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
return GS_ALL_DONE;
}
+/* Gimplify the LABEL_EXPR pointed to by EXPR_P. */
+
+static enum gimplify_status
+gimplify_label_expr (tree *expr_p, gimple_seq *pre_p)
+{
+ gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
+ == current_function_decl);
+
+ glabel *label_stmt = gimple_build_label (LABEL_EXPR_LABEL (*expr_p));
+ gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p));
+ gimplify_seq_add_stmt (pre_p, label_stmt);
+
+ return GS_ALL_DONE;
+}
+
/* Gimplify the CASE_LABEL_EXPR pointed to by EXPR_P. */
static enum gimplify_status
@@ -1711,6 +2158,7 @@ gimplify_case_label_expr (tree *expr_p, gimple_seq *pre_p)
break;
label_stmt = gimple_build_label (CASE_LABEL (*expr_p));
+ gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p));
ctxp->case_labels.safe_push (*expr_p);
gimplify_seq_add_stmt (pre_p, label_stmt);
@@ -10777,11 +11225,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
break;
case LABEL_EXPR:
- ret = GS_ALL_DONE;
- gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
- == current_function_decl);
- gimplify_seq_add_stmt (pre_p,
- gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+ ret = gimplify_label_expr (expr_p, pre_p);
break;
case CASE_LABEL_EXPR:
diff --git a/gcc/godump.c b/gcc/godump.c
index e3448a1..608542c 100644
--- a/gcc/godump.c
+++ b/gcc/godump.c
@@ -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;
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c269ca6..029a534 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -244,6 +244,15 @@ expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
gcc_unreachable ();
}
+/* This should get expanded in the lower pass. */
+
+static void
+expand_FALLTHROUGH (internal_fn, gcall *call)
+{
+ error_at (gimple_location (call),
+ "invalid use of attribute %<fallthrough%>");
+}
+
/* Helper function for expand_addsub_overflow. Return 1
if ARG interpreted as signed in its precision is known to be always
positive or 2 if ARG is known to be always negative, or 3 if ARG may
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 6701cd9..d4fbdb2 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -195,6 +195,9 @@ DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_COMPLEMENT, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_RESET, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ATOMIC_COMPARE_EXCHANGE, ECF_LEAF | ECF_NOTHROW, NULL)
+/* To implement [[fallthrough]]. */
+DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF | ECF_NOTHROW, NULL)
+
#undef DEF_INTERNAL_INT_FN
#undef DEF_INTERNAL_FLT_FN
#undef DEF_INTERNAL_OPTAB_FN
diff --git a/gcc/langhooks.c b/gcc/langhooks.c
index 538d9f9..79a846c 100644
--- a/gcc/langhooks.c
+++ b/gcc/langhooks.c
@@ -725,3 +725,12 @@ lang_GNU_Fortran (void)
{
return strncmp (lang_hooks.name, "GNU Fortran", 11) == 0;
}
+
+/* Returns true if the current lang_hooks represents the GNU Objective-C
+ frontend. */
+
+bool
+lang_GNU_OBJC (void)
+{
+ return strncmp (lang_hooks.name, "GNU Objective-C", 15) == 0;
+}
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index c109c8c..cfaee62 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -546,5 +546,6 @@ extern tree add_builtin_type (const char *name, tree type);
extern bool lang_GNU_C (void);
extern bool lang_GNU_CXX (void);
extern bool lang_GNU_Fortran (void);
-
+extern bool lang_GNU_OBJC (void);
+
#endif /* GCC_LANG_HOOKS_H */
diff --git a/gcc/reload1.c b/gcc/reload1.c
index da53cfa..55aafe4 100644
--- a/gcc/reload1.c
+++ b/gcc/reload1.c
@@ -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:
diff --git a/gcc/resource.c b/gcc/resource.c
index 1d7ce95..32e3e44 100644
--- a/gcc/resource.c
+++ b/gcc/resource.c
@@ -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:
diff --git a/gcc/system.h b/gcc/system.h
index 8a17197..8ca71cf 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -746,6 +746,12 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
#define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
#endif
+#if GCC_VERSION >= 7000
+# define gcc_fallthrough() __attribute__((fallthrough))
+#else
+# define gcc_fallthrough()
+#endif
+
#if GCC_VERSION >= 3001
#define STATIC_CONSTANT_P(X) (__builtin_constant_p (X) && (X))
#else
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 45e5307..2dd006e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..b45880f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
new file mode 100644
index 0000000..99e44d9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c
@@ -0,0 +1,239 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ break;
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ else
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else if (i > 10)
+ {
+ bar (3);
+ __attribute__((fallthrough));
+ }
+ else
+ break;
+ case 2:
+ bar (4);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ break;
+ }
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ {
+ bar (1);
+ }
+ else
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ break;
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ break;
+ }
+ else
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ break;
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ break;
+ }
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ break;
+ }
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ else
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ {
+ bar (1);
+ bar (2);
+ }
+ else
+ __attribute__((fallthrough));
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ {
+ bar (0);
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ }
+ else
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ else if (i > 10)
+ {
+ bar (1);
+ break;
+ }
+ else
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (0);
+ break;
+ }
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ {
+ bar (1);
+ }
+ else
+ {
+ bar (1);
+ __attribute__((fallthrough));
+ }
+ case 2:
+ bar (99);
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
new file mode 100644
index 0000000..e8f47f5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c
@@ -0,0 +1,23 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough -O2" } */
+
+/* Prevent false positive with optimizations. */
+
+extern void g (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i > 10)
+ g (0);
+ else
+ goto L;
+ break;
+L:
+ case 2:;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
new file mode 100644
index 0000000..91a68ab
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c
@@ -0,0 +1,26 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough -O2" } */
+
+/* Don't let optimizations preclude the warning. */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i > 1)
+ bar (1);
+ else
+ goto D;
+ break;
+ case 2:
+ bar (2); /* { dg-warning "statement may fall through" } */
+ D:
+ default:
+ bar (33);
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
new file mode 100644
index 0000000..f3ec79f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c
@@ -0,0 +1,63 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* As per <http://security.coverity.com/blog/2013/Sep/gimme-a-break.html>, don't
+ warn for terminated branches (fall through to break / end of the switch). */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ return;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ goto X;
+ }
+X:
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ break;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ case 2:
+ case 3:
+ default:
+ break;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ case 2:
+ case 3:
+ default:;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
new file mode 100644
index 0000000..b7c825b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c
@@ -0,0 +1,162 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Test various falls through comments. */
+
+extern void bar (int);
+
+void
+fn (int i)
+{
+ switch (i)
+ {
+ case -1:
+ bar (-1);
+ /*-fallthrough*/
+ case 0:
+ bar (0);
+ /*@fallthrough@*/
+ case 1:
+ bar (1);
+ /* FALL THRU */
+ case 2:
+ bar (2);
+ /* FALLTHRU */
+ case 3:
+ bar (3);
+ /* FALLS THRU */
+ case 4:
+ bar (4);
+ /* FALL-THRU */
+ case 5:
+ bar (5);
+ /* FALL THROUGH */
+ case 6:
+ bar (6);
+ /* FALLTHROUGH */
+ case 7:
+ bar (7);
+ /* FALLS THROUGH */
+ case 8:
+ bar (8);
+ /* FALL-THROUGH */
+ case 9:
+ bar (9);
+ /*FALLTHRU*/
+ case 10:
+ bar (10);
+ /* FALLTHRU.*/
+ case 11:
+ bar (11);
+ /* FALLTHROUGH. */
+ case 12:
+ bar (12);
+ /* Fall thru */
+ case 13:
+ bar (13);
+ /* Falls thru */
+ case 14:
+ bar (14);
+ /* Fall-thru */
+ case 15:
+ bar (15);
+ /* Fall Thru */
+ case 16:
+ bar (16);
+ /* Falls Thru */
+ case 17:
+ bar (17);
+ /* Fall-Thru */
+ case 18:
+ bar (18);
+ /* Fall through */
+ case 19:
+ bar (19);
+ /* Falls through */
+ case 20:
+ bar (20);
+ /* Fall-through */
+ case 21:
+ bar (21);
+ /* Fall Through */
+ case 22:
+ bar (22);
+ /* Falls Through */
+ case 23:
+ bar (23);
+ /* Fall-Through */
+ case 24:
+ bar (24);
+ /* Falls through. */
+ case 25:
+ bar (25);
+ /* Falls through. */
+ case 26:
+ bar (26);
+ /* fall thru */
+ case 27:
+ bar (27);
+ /* falls thru */
+ case 28:
+ bar (28);
+ /* fall-thru */
+ case 29:
+ bar (29);
+ /* fall thru */
+ case 30:
+ bar (30);
+ /* falls thru */
+ case 31:
+ bar (31);
+ /* fall-thru */
+ case 32:
+ bar (32);
+ /* fall through */
+ case 33:
+ bar (33);
+ /* falls through */
+ case 34:
+ bar (34);
+ /* fall-through */
+ default:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 0:
+ i++;
+ /*@fallthrough@*/
+L:
+ default:
+ bar (6);
+ }
+
+ {
+ __label__ L2;
+ switch (i)
+ {
+ case 0:
+ i++;
+ /*@fallthrough@*/
+L2:
+ default:
+ bar (6);
+ }
+ }
+
+ /* Don't generate false -Wswitch-unreachable warning. */
+ switch (i)
+ {
+ /*FALLTHROUGH*/
+ case 0:
+ i++;
+ }
+
+ if (i)
+ {
+ /* fall through */
+ L1:;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
new file mode 100644
index 0000000..ee3e52d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c
@@ -0,0 +1,31 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch. Check that we don't warn here. */
+
+void
+f (int i)
+{
+ int j = 0;
+ switch (i)
+ {
+ case 0:
+ case 1:
+ j = 10;
+ __attribute__((fallthrough));
+ case 2:
+ j += 10;
+ break;
+ case 3:
+ switch (i)
+ {
+ case 5:
+ j += 2;
+ __attribute__((fallthrough));
+ case 6:
+ j += 4;
+ break;
+ }
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
new file mode 100644
index 0000000..923f012
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c
@@ -0,0 +1,32 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch, and with an initialization on top. Check that
+ we do warn here. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ {
+ int t = 3;
+ switch (i)
+ {
+ case 3:
+ if (i > 5)
+ --i;
+ i += 10; /* { dg-warning "statement may fall through" } */
+ case 4:
+ t /= 5;
+ break;
+ }
+ break;
+ }
+ case 2:
+ --i;
+ break;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
new file mode 100644
index 0000000..23ff5f1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c
@@ -0,0 +1,29 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Another nested switch, and with an initialization on top. Check that
+ we do not warn here as the case 3 falls through to break. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ {
+ int t = 3;
+ switch (i)
+ {
+ case 3:
+ i += 10;
+ case 4:
+ break;
+ }
+ break;
+ }
+ case 2:
+ --i;
+ break;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
new file mode 100644
index 0000000..2c8a3cb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c
@@ -0,0 +1,42 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Testing some loops. */
+
+int f (void);
+
+int
+g (int i)
+{
+ switch (i)
+ {
+ case 0:
+ for (;;)
+ {
+ if (f ()) /* { dg-warning "statement may fall through" "fall through" { xfail *-*-* } } */
+ break;
+ }
+ case 1:
+ return 1;
+ }
+ return 0;
+}
+
+int
+h (int i)
+{
+ switch (i)
+ {
+ case 0:
+ do
+ {
+ if (f ()) /* { dg-warning "statement may fall through" } */
+ break;
+ }
+ while (0);
+ case 1:
+ return 1;
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
new file mode 100644
index 0000000..b7a3791
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c
@@ -0,0 +1,85 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Testing non-case labels. */
+
+int foo (int);
+
+void
+f1 (int i)
+{
+ switch (i)
+ {
+ case 0:
+ foo (1);
+ L1:
+ foo (2);
+ }
+
+ switch (i)
+ {
+ case 0:
+ foo (1); /* { dg-warning "statement may fall through" } */
+ L2:
+ case 2:
+ foo (2);
+ }
+
+ switch (i)
+ {
+ case 0:
+ foo (1); /* { dg-warning "statement may fall through" } */
+ case 2:
+ L3:
+ foo (2);
+ }
+
+ switch (i)
+ {
+ case 0:
+ foo (1); /* { dg-warning "statement may fall through" } */
+ L4:
+ case 2:
+ L5:
+ foo (2);
+ }
+
+ switch (i)
+ {
+ case 0:
+ switch (i)
+ {
+ case 1:
+ foo (2);
+ L6:
+ foo (3);
+ }
+ }
+
+ switch (i)
+ {
+ case 0:
+ switch (i)
+ {
+ case 1:
+ foo (2); /* { dg-warning "statement may fall through" } */
+ L7:
+ case 2:
+ foo (3);
+ }
+ }
+
+ switch (i)
+ {
+ case 0:
+ switch (i)
+ {
+ case 1:
+ foo (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ L8:
+ foo (3);
+ }
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
new file mode 100644
index 0000000..4dfb278
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c
@@ -0,0 +1,223 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if without else. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ goto L1;
+ case 2:
+L1:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ goto L2;
+L2:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L3;
+ break;
+ case 2:
+L3:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L4;
+ break;
+L4:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ if (i > 9)
+ bar (1);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ if (i > 9)
+ bar (1);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ { int a; }
+ {
+ if (i) /* { dg-warning "statement may fall through" } */
+ if (i > 9)
+ bar (1);
+ }
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ bar (2);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ bar (2);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ if (i)
+ bar (2);
+ if (i)
+ bar (3);
+ bar (4); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ if (i)
+ bar (2);
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (3);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ if (i)
+ bar (2);
+ if (i)
+ bar (3);
+ bar (4);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ if (i)
+ bar (2);
+ if (i)
+ bar (3);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
new file mode 100644
index 0000000..d37a840
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c
@@ -0,0 +1,41 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+int
+f (int i)
+{
+ switch (i)
+ {
+ case -1:
+ __attribute__((fallthrough));
+ default:
+ __attribute__((fallthrough));
+ case 1:
+ return 6;
+ case 2 ... 4:
+ __attribute__((fallthrough));
+ case 5:
+ return 7;
+ }
+ return 0;
+}
+
+int
+g (int i)
+{
+ switch (i)
+ {
+ case -1:
+ __attribute__((used)); /* { dg-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
index 0000000..6092a90
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c
@@ -0,0 +1,25 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+int
+f (int i)
+{
+ switch (i)
+ {
+ case 0:
+ i++;
+ __attribute__((fallthrough));
+ lab1:
+ case 1:
+ i++;
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ lab2:
+ --i;
+ break;
+ case 3:
+ i++;
+ break;
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
new file mode 100644
index 0000000..fbb9712
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c
@@ -0,0 +1,543 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if with else. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else
+ bar (2);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ bar (2);
+ bar (3); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ bar (2);
+ bar (3); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ return;
+ bar (3); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ return;
+ bar (3); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ {
+ bar (1);
+ bar (2);
+ bar (3);
+ bar (4);
+ }
+ else
+ {
+ bar (5);
+ bar (6);
+ bar (7);
+ bar (8);
+ }
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (1);
+ bar (2);
+ bar (3);
+ bar (4);
+ }
+ else
+ {
+ bar (5);
+ bar (6);
+ bar (7);
+ bar (8);
+ }
+ bar (9); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ {
+ }
+ else
+ bar (2);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else
+ {
+ }
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ {
+ }
+ else
+ {
+ }
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ return;
+ else
+ {
+ }
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ {
+ }
+ else
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L1;
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ case 2:
+L1:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L2;
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+L2:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else
+ goto L3;
+ case 2:
+L3:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else
+ goto L4;
+L4:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L5;
+ else
+ goto L5;
+L5:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ bar (2);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ bar (2);
+ bar (3);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ bar (2);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ bar (2);
+ bar (3);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ return;
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ return;
+ bar (3);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ return;
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ return;
+ bar (3);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (1);
+ bar (2);
+ bar (3);
+ bar (4);
+ }
+ else
+ {
+ bar (5);
+ bar (6);
+ bar (7);
+ bar (8);
+ }
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ bar (1);
+ bar (2);
+ bar (3);
+ bar (4);
+ }
+ else
+ {
+ bar (5);
+ bar (6);
+ bar (7);
+ bar (8);
+ }
+ bar (9);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ }
+ else
+ bar (2);
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ {
+ }
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ }
+ else
+ {
+ }
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ return;
+ else
+ {
+ }
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ {
+ }
+ else
+ return;
+ break;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L6;
+ else
+ bar (2);
+ break;
+ case 2:
+L6:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L7;
+ else
+ bar (2);
+ break;
+L7:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ goto L8;
+ break;
+ case 2:
+L8:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ bar (1);
+ else
+ goto L9;
+ break;
+L9:
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i)
+ goto L10;
+ else
+ goto L10;
+ break;
+L10:
+ case 2:
+ __builtin_abort ();
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
new file mode 100644
index 0000000..9a0aeb7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c
@@ -0,0 +1,250 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+/* Test if with more elses. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ bar (2);
+ else if (i > 15)
+ bar (3);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ bar (2);
+ else if (i > 15)
+ bar (3);
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else if (i > 15)
+ bar (3);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else if (i > 15)
+ bar (3);
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ bar (3);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ bar (3);
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ bar (4);
+ else if (i > 15)
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ bar (4);
+ else if (i > 15)
+ return;
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10)
+ return;
+ else if (i > 15) /* { dg-warning "statement may fall through" } */
+ bar (3);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10)
+ return;
+ else if (i > 15) /* { dg-warning "statement may fall through" } */
+ bar (3);
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else if (i > 15)
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else if (i > 15)
+ return;
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5) /* { dg-warning "statement may fall through" } */
+ bar (1);
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ return;
+ else
+ bar (4);
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10)
+ return;
+ else if (i > 15) /* { dg-warning "statement may fall through" } */
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ return;
+ else
+ bar (4); /* { dg-warning "statement may fall through" } */
+ case 2:
+ __builtin_abort ();
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 5)
+ return;
+ else if (i > 10)
+ return;
+ else if (i > 15)
+ return;
+ else
+ return;
+ case 2:
+ __builtin_abort ();
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
new file mode 100644
index 0000000..9317484
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c
@@ -0,0 +1,109 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+extern void die (void) __attribute__((noreturn));
+
+/* Test may_fallthru-ness. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (0);
+ __attribute__((fallthrough));
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (0);
+ return;
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (0);
+ break;
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (0);
+ goto L1;
+L1:
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (0);
+ die ();
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int i, j, k;
+ bar (0);
+ __attribute__((fallthrough));
+ }
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int i, j, k;
+ bar (0);
+ return;
+ }
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int i, j, k;
+ bar (0);
+ break;
+ }
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int i, j, k;
+ bar (0);
+ goto L2;
+ }
+L2:
+ case 2:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int i, j, k;
+ bar (0);
+ die ();
+ }
+ case 2:;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
new file mode 100644
index 0000000..8364c1b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c
@@ -0,0 +1,305 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+
+/* Test nested scopes. */
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ {
+ int j;
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 10; /* { dg-warning "statement may fall through" } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int k = 9;
+ k++;
+ {
+ int j = 10;
+ j++; /* { dg-warning "statement may fall through" } */
+ }
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int k = 9;
+ k++;
+ {
+ int j = 10;
+ j++;
+ {
+ bar (1); /* { dg-warning "statement may fall through" } */
+ }
+ }
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ __attribute__((fallthrough));
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ {
+ int k = j + 5;
+ bar (k);
+ __attribute__((fallthrough));
+ }
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ return;
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ goto L1;
+ }
+L1:
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ { /* { dg-warning "statement may fall through" "" { target c } 120 } */
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return; /* { dg-warning "statement may fall through" "" { target c++ } 124 } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return;
+ else
+ return;
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ { /* { dg-warning "statement may fall through" "" { target c } 148 } */
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ bar (1);
+ else
+ return; /* { dg-warning "statement may fall through" "" { target c++ } 154 } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return;
+ else
+ bar (2); /* { dg-warning "statement may fall through" } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ { /* { dg-warning "statement may fall through" "" { target c } 178 } */
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ bar (1);
+ else
+ bar (2); /* { dg-warning "statement may fall through" "" { target c++ } 184 } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return;
+ }
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return;
+ else
+ return;
+ }
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ bar (1);
+ else
+ return;
+ }
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ return;
+ else
+ bar (2);
+ }
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ bar (1);
+ else
+ bar (2);
+ }
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 9;
+ while (1);
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ { /* { dg-warning "statement may fall through" "" { target c } 282 } */
+ int j = 9;
+ switch (j); /* { dg-warning "statement may fall through" "" { target c++ } 284 } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ int j = 0;
+ bar (j);
+ if (j == 8)
+ bar (1);
+ else
+ bar (2);
+ __attribute__((fallthrough));
+ }
+ case 2:
+ bar (99);
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
new file mode 100644
index 0000000..21a158c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c
@@ -0,0 +1,124 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void bar (int);
+extern int bar2 (void);
+extern int *map;
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (0); /* { dg-warning "statement may fall through" } */
+ static int i = 10;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ { /* { dg-warning "statement may fall through" "" { target c } 23 } */
+ int a[i]; /* { dg-warning "statement may fall through" "" { target c++ } 24 } */
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ for (int j = 0; j < 10; j++) /* { dg-warning "statement may fall through" "" { target c } 33 } */
+ map[j] = j; /* { dg-warning "statement may fall through" "" { target c++ } 34 } */
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ do /* { dg-warning "statement may fall through" "" { target c++ } 42 } */
+ bar (2);
+ while (--i); /* { dg-warning "statement may fall through" "" { target c } 44 } */
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ {
+ switch (i + 2)
+ case 4:
+ bar (1); /* { dg-warning "statement may fall through" } */
+ case 5:
+ bar (5);
+ return;
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:;
+ case 2:;
+ }
+
+ switch (i)
+ {
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i & 1) /* { dg-warning "statement may fall through" } */
+ {
+ bar (23);
+ break;
+ }
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ if (i > 9) /* { dg-warning "statement may fall through" } */
+ {
+ bar (9);
+ if (i == 10)
+ {
+ bar (10);
+ break;
+ }
+ }
+ case 2:
+ bar (99);
+ }
+
+ int r;
+ switch (i)
+ {
+ case 1:
+ r = bar2 ();
+ if (r) /* { dg-warning "statement may fall through" } */
+ break;
+ case 2:
+ bar (99);
+ }
+
+ switch (i)
+ {
+ case 1:
+ r = bar2 ();
+ if (r)
+ return;
+ if (!i) /* { dg-warning "statement may fall through" } */
+ return;
+ case 2:
+ bar (99);
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
new file mode 100644
index 0000000..0ed7928
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c
@@ -0,0 +1,101 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+extern void grace (int);
+
+int
+fn1 (int i)
+{
+ switch (i)
+ case 1:
+ if (i == 5)
+ grace (0);
+ else
+ goto done;
+done:;
+}
+
+int
+fn2 (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i == 5) /* { dg-warning "statement may fall through" } */
+ grace (0);
+ else
+ goto done;
+ case 2:
+ --i;
+ }
+done:;
+}
+
+int
+fn3 (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i == 5)
+ goto done;
+ else
+ goto done;
+ }
+done:;
+}
+
+int
+fn4 (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i == 5)
+ {
+ grace (1);
+ goto done;
+ }
+ else
+ goto done;
+ case 2:;
+ }
+done:;
+}
+
+int
+fn5 (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i == 5)
+ {
+ grace (1);
+ goto done;
+ }
+ else
+ grace (4); /* { dg-warning "statement may fall through" } */
+ case 2:
+ grace (9);
+ }
+done:;
+}
+
+int
+fn6 (int i)
+{
+ switch (i)
+ {
+ case 1:
+ if (i == 5) /* { dg-warning "statement may fall through" } */
+ {
+ grace (1);
+ goto done;
+ }
+ case 2:
+ grace (8);
+ }
+done:;
+}
diff --git a/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
new file mode 100644
index 0000000..394d699
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c
@@ -0,0 +1,26 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* Test we don't remove FALLTHROUGH () too early. */
+
+extern void h (int);
+
+void
+g (int i)
+{
+ switch (i)
+ {
+ case 1:
+ {
+ switch (i)
+ {
+ case 3:
+ h (7);
+ __attribute__((fallthrough));
+ case 4:;
+ }
+ }
+ case 2:;
+ }
+}
diff --git a/gcc/testsuite/c-c++-common/attr-fallthrough-1.c b/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
new file mode 100644
index 0000000..ecfd094
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-fallthrough-1.c
@@ -0,0 +1,57 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic" } */
+
+extern void bar (int);
+void
+fn (int i)
+{
+ __attribute__((fallthrough)) int j = 0; /* { dg-warning "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
index 0000000..959564b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
@@ -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
index 0000000..523067e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/fallthrough1.C
@@ -0,0 +1,57 @@
+// PR c/7652
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wextra -Wall -Wpedantic" }
+
+extern void bar (int);
+void
+fn (int i)
+{
+ [[gnu::fallthrough]] int j = 0; // { dg-warning "attribute ignored" }
+
+ if (j)
+ [[gnu::fallthrough]]; // { dg-error "invalid use" }
+
+ [[gnu::fallthrough]]; // { dg-error "invalid use" }
+ switch (i)
+ {
+ [[gnu::fallthrough]]; // { dg-warning "statement will never" }
+ case 1:
+ i++;
+ [[gnu::fallthrough]];
+ case 2:
+ if (i) // { dg-warning "statement may fall through" }
+ bar (2);
+ else
+ [[gnu::fallthrough]];
+ case 3:
+ if (i > 1)
+ [[gnu::fallthrough]];
+ else
+ return;
+ case 4:
+ if (i)
+ [[gnu::fallthrough]]; // { dg-warning "not preceding" }
+ [[gnu::fallthrough]];
+ case 5:
+ ;
+ [[gnu::fallthrough]];
+ case 6:
+ if (i) // { dg-warning "statement may fall through" }
+ bar (6);
+ else
+ {
+ [[gnu::fallthrough]];
+ }
+ case 7:
+ if (i > 1)
+ {
+ [[gnu::fallthrough]];
+ }
+ else
+ bar (7); // { dg-warning "statement may fall through" }
+ default:
+ --j;
+ }
+
+ [[gnu::fallthrough]]; // { dg-error "invalid use" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C b/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
new file mode 100644
index 0000000..b6964e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/fallthrough2.C
@@ -0,0 +1,21 @@
+// PR c/7652
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wextra -Wall -Wpedantic" }
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ [[fallthrough]]; // { dg-warning ".fallthrough. is a C\\+\\+17 feature" }
+ case 3:
+ bar (1);
+ [[gnu::fallthrough, gnu::fallthrough]]; // { dg-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
index 0000000..d15b1ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/fallthrough1.C
@@ -0,0 +1,20 @@
+// PR c/7652
+// { dg-do compile }
+// { dg-options "-std=c++1z -Wextra -Wall -Wpedantic" }
+
+// Check that we accept attribute [[fallthrough]].
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ [[fallthrough]];
+ case 2:
+ bar (2);
+ }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wunused-label-1.C b/gcc/testsuite/g++.dg/warn/Wunused-label-1.C
index 96f49b3..255a26e 100644
--- a/gcc/testsuite/g++.dg/warn/Wunused-label-1.C
+++ b/gcc/testsuite/g++.dg/warn/Wunused-label-1.C
@@ -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
index 0000000..f8b54f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c
@@ -0,0 +1,22 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough -Wdeclaration-after-statement" } */
+
+/* Test we don't print bogus "mixed declarations and code" warning. */
+
+int
+f (int b)
+{
+ switch (b)
+ {
+ case 0:
+ b++;
+ __attribute__((fallthrough));
+ case 1:
+ b--;
+ __attribute__((unused)) int a; /* { dg-warning "mixed declarations and code" } */
+ case 2:
+ break;
+ }
+ return 99;
+}
diff --git a/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm b/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
new file mode 100644
index 0000000..b45880f
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
diff --git a/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m b/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
new file mode 100644
index 0000000..b45880f
--- /dev/null
+++ b/gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 8b3e5cc..353a625 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1077,6 +1077,9 @@ struct GTY(()) tree_base {
TRANSACTION_EXPR_RELAXED in
TRANSACTION_EXPR
+ FALLTHROUGH_LABEL_P in
+ LABEL_DECL
+
private_flag:
TREE_PRIVATE in
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index 6886790..7f78734 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -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);
diff --git a/gcc/tree.h b/gcc/tree.h
index 38ee816..0d9ad01 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -774,6 +774,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
computed gotos. */
#define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
+/* Whether a case or a user-defined label is allowed to fall through to.
+ This is used to implement -Wimplicit-fallthrough. */
+#define FALLTHROUGH_LABEL_P(NODE) \
+ (LABEL_DECL_CHECK (NODE)->base.public_flag)
+
/* Nonzero means this expression is volatile in the C sense:
its address should be of type `volatile WHATEVER *'.
In other words, the declared item is volatile qualified.
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 4da9e21..3972790 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -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:
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 61304cb..94ea99d 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -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
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index cfc6ccd..6352ac5 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -185,7 +185,8 @@ struct GTY(()) cpp_string {
#define STRINGIFY_ARG (1 << 2) /* If macro argument to be stringified. */
#define PASTE_LEFT (1 << 3) /* If on LHS of a ## operator. */
#define NAMED_OP (1 << 4) /* C++ named operators. */
-#define NO_EXPAND (1 << 5) /* Do not macro-expand this token. */
+#define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH
+ comment. */
#define BOL (1 << 6) /* Token at beginning of line. */
#define PURE_ZERO (1 << 7) /* Single 0 digit, used by the C++ frontend,
set in c-lex.c. */
@@ -193,6 +194,7 @@ struct GTY(()) cpp_string {
#define SP_PREV_WHITE (1 << 9) /* If whitespace before a ##
operator, or before this token
after a # operator. */
+#define NO_EXPAND (1 << 10) /* Do not macro-expand this token. */
/* Specify which field, if any, of the cpp_token union is used. */
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 6254ed6..0c47e29 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -2032,6 +2032,94 @@ save_comment (cpp_reader *pfile, cpp_token *token, const unsigned char *from,
store_comment (pfile, token);
}
+/* Returns true if comment at COMMENT_START is a recognized FALLTHROUGH
+ comment. */
+
+static bool
+fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
+{
+ const unsigned char *from = comment_start + 1;
+ /* Whole comment contents:
+ -fallthrough
+ @fallthrough@
+ */
+ if (*from == '-' || *from == '@')
+ {
+ size_t len = sizeof "fallthrough" - 1;
+ if ((size_t) (pfile->buffer->cur - from - 1) < len)
+ return false;
+ if (memcmp (from + 1, "fallthrough", len))
+ return false;
+ if (*from == '@')
+ {
+ if (from[len + 1] != '@')
+ return false;
+ len++;
+ }
+ from += 1 + len;
+ }
+ /* Whole comment contents (regex):
+ [ \t]*FALL(S | |-)?THR(OUGH|U)\.?[ \t]*
+ [ \t]*Fall(s | |-)?[Tt]hr(ough|u)\.?[ \t]*
+ [ \t]*fall(s | |-)?thr(ough|u)\.?[ \t]*
+ */
+ else
+ {
+ while (*from == ' ' || *from == '\t')
+ from++;
+ unsigned char f = *from;
+ if (f != 'F' && f != 'f')
+ return false;
+ if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthrough")
+ return false;
+ bool all_upper = false;
+ if (f == 'F' && memcmp (from + 1, "ALL", sizeof "ALL" - 1) == 0)
+ all_upper = true;
+ else if (memcmp (from + 1, "all", sizeof "all" - 1))
+ return false;
+ if (from[sizeof "fall" - 1] == (all_upper ? 'S' : 's')
+ && from[sizeof "falls" - 1] == ' ')
+ from += sizeof "falls " - 1;
+ else if (from[sizeof "fall" - 1] == ' '
+ || from[sizeof "fall" - 1] == '-')
+ from += sizeof "fall " - 1;
+ else if (from[sizeof "fall" - 1] != (all_upper ? 'T' : 't'))
+ return false;
+ else
+ from += sizeof "fall" - 1;
+ if ((f == 'f' || *from != 'T') && (all_upper || *from != 't'))
+ return false;
+ if ((size_t) (pfile->buffer->cur - from) < sizeof "thru")
+ return false;
+ if (memcmp (from + 1, all_upper ? "HRU" : "hru", sizeof "hru" - 1))
+ {
+ if ((size_t) (pfile->buffer->cur - from) < sizeof "through")
+ return false;
+ if (memcmp (from + 1, all_upper ? "HROUGH" : "hrough",
+ sizeof "hrough" - 1))
+ return false;
+ from += sizeof "through" - 1;
+ }
+ else
+ from += sizeof "thru" - 1;
+ if (*from == '.')
+ from++;
+ while (*from == ' ' || *from == '\t')
+ from++;
+ }
+ /* C block comment. */
+ if (*comment_start == '*')
+ {
+ if (*from != '*' || from[1] != '/')
+ return false;
+ }
+ /* C++ line comment. */
+ else if (*from != '\n')
+ return false;
+
+ return true;
+}
+
/* Allocate COUNT tokens for RUN. */
void
_cpp_init_tokenrun (tokenrun *run, unsigned int count)
@@ -2310,7 +2398,7 @@ _cpp_lex_direct (cpp_reader *pfile)
{
cppchar_t c;
cpp_buffer *buffer;
- const unsigned char *comment_start;
+ const unsigned char *comment_start = NULL;
cpp_token *result = pfile->cur_token++;
fresh_line:
@@ -2337,6 +2425,8 @@ _cpp_lex_direct (cpp_reader *pfile)
}
return result;
}
+ if (buffer != pfile->buffer)
+ comment_start = NULL;
if (!pfile->keep_tokens)
{
pfile->cur_run = &pfile->base_run;
@@ -2443,6 +2533,11 @@ _cpp_lex_direct (cpp_reader *pfile)
result->flags |= NAMED_OP;
result->type = (enum cpp_ttype) result->val.node.node->directive_index;
}
+
+ /* Signal FALLTHROUGH comment followed by another token. */
+ if (comment_start
+ && fallthrough_comment_p (pfile, comment_start))
+ result->flags |= PREV_FALLTHROUGH;
break;
case '\'':
@@ -2534,6 +2629,9 @@ _cpp_lex_direct (cpp_reader *pfile)
goto update_tokens_line;
}
+ if (fallthrough_comment_p (pfile, comment_start))
+ result->flags |= PREV_FALLTHROUGH;
+
/* Save the comment as a token in its own right. */
save_comment (pfile, result, comment_start, c);
break;
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 6f134cb..230f764 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -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
diff --git a/libstdc++-v3/libsupc++/hash_bytes.cc b/libstdc++-v3/libsupc++/hash_bytes.cc
index 2e5bbfa..1042de6 100644
--- a/libstdc++-v3/libsupc++/hash_bytes.cc
+++ b/libstdc++-v3/libsupc++/hash_bytes.cc
@@ -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;