From 81fea426da8c4687bb32e6894dc26f00ae211822 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Mon, 26 Sep 2016 09:42:50 +0000 Subject: Implement -Wimplicit-fallthrough. Co-Authored-By: Jakub Jelinek From-SVN: r240485 --- gcc/ChangeLog | 56 +++ gcc/Makefile.in | 5 + gcc/builtins.c | 5 +- gcc/c-family/ChangeLog | 8 + gcc/c-family/c-common.c | 42 ++ gcc/c-family/c-common.h | 1 + gcc/c/ChangeLog | 16 + gcc/c/c-decl.c | 2 +- gcc/c/c-parser.c | 97 +++- gcc/common.opt | 4 + gcc/config/rs6000/rs6000.c | 2 +- gcc/convert.c | 4 +- gcc/cp/ChangeLog | 30 ++ gcc/cp/constexpr.c | 2 + gcc/cp/constraint.cc | 3 +- gcc/cp/parser.c | 81 ++- gcc/cp/pt.c | 13 +- gcc/cp/semantics.c | 1 + gcc/cp/typeck.c | 4 +- gcc/doc/extend.texi | 62 ++- gcc/doc/invoke.texi | 91 +++- gcc/final.c | 2 + gcc/fortran/ChangeLog | 12 + gcc/fortran/arith.c | 2 +- gcc/fortran/frontend-passes.c | 3 +- gcc/fortran/parse.c | 2 +- gcc/fortran/primary.c | 1 + gcc/fortran/trans-array.c | 1 + gcc/fortran/trans-expr.c | 1 + gcc/fortran/trans-io.c | 1 + gcc/genattrtab.c | 2 + gcc/genpreds.c | 2 +- gcc/gimple-ssa-strength-reduction.c | 2 +- gcc/gimple.h | 10 + gcc/gimplify.c | 454 ++++++++++++++++- gcc/godump.c | 1 + gcc/internal-fn.c | 9 + gcc/internal-fn.def | 3 + gcc/langhooks.c | 9 + gcc/langhooks.h | 3 +- gcc/reload1.c | 1 + gcc/resource.c | 2 + gcc/system.h | 6 + gcc/testsuite/ChangeLog | 34 ++ .../c-c++-common/Wimplicit-fallthrough-1.c | 38 ++ .../c-c++-common/Wimplicit-fallthrough-10.c | 239 +++++++++ .../c-c++-common/Wimplicit-fallthrough-11.c | 23 + .../c-c++-common/Wimplicit-fallthrough-12.c | 26 + .../c-c++-common/Wimplicit-fallthrough-13.c | 63 +++ .../c-c++-common/Wimplicit-fallthrough-14.c | 162 ++++++ .../c-c++-common/Wimplicit-fallthrough-15.c | 31 ++ .../c-c++-common/Wimplicit-fallthrough-16.c | 32 ++ .../c-c++-common/Wimplicit-fallthrough-17.c | 29 ++ .../c-c++-common/Wimplicit-fallthrough-18.c | 42 ++ .../c-c++-common/Wimplicit-fallthrough-19.c | 85 ++++ .../c-c++-common/Wimplicit-fallthrough-2.c | 223 +++++++++ .../c-c++-common/Wimplicit-fallthrough-20.c | 41 ++ .../c-c++-common/Wimplicit-fallthrough-21.c | 25 + .../c-c++-common/Wimplicit-fallthrough-3.c | 543 +++++++++++++++++++++ .../c-c++-common/Wimplicit-fallthrough-4.c | 250 ++++++++++ .../c-c++-common/Wimplicit-fallthrough-5.c | 109 +++++ .../c-c++-common/Wimplicit-fallthrough-6.c | 305 ++++++++++++ .../c-c++-common/Wimplicit-fallthrough-7.c | 124 +++++ .../c-c++-common/Wimplicit-fallthrough-8.c | 101 ++++ .../c-c++-common/Wimplicit-fallthrough-9.c | 26 + gcc/testsuite/c-c++-common/attr-fallthrough-1.c | 57 +++ gcc/testsuite/c-c++-common/attr-fallthrough-2.c | 54 ++ gcc/testsuite/g++.dg/cpp0x/fallthrough1.C | 57 +++ gcc/testsuite/g++.dg/cpp0x/fallthrough2.C | 21 + gcc/testsuite/g++.dg/cpp1z/fallthrough1.C | 20 + gcc/testsuite/g++.dg/warn/Wunused-label-1.C | 2 +- gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c | 22 + .../obj-c++.dg/Wimplicit-fallthrough-1.mm | 38 ++ gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m | 38 ++ gcc/tree-core.h | 3 + gcc/tree-ssa-loop-ivopts.c | 2 +- gcc/tree.h | 5 + gcc/varasm.c | 2 +- libcpp/ChangeLog | 10 + libcpp/include/cpplib.h | 4 +- libcpp/lex.c | 100 +++- libstdc++-v3/ChangeLog | 5 + libstdc++-v3/libsupc++/hash_bytes.cc | 2 + 83 files changed, 4005 insertions(+), 46 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-1.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-10.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-11.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-12.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-13.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-14.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-15.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-16.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-17.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-18.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-19.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-2.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-20.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-21.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-3.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-4.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-5.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-6.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-7.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-8.c create mode 100644 gcc/testsuite/c-c++-common/Wimplicit-fallthrough-9.c create mode 100644 gcc/testsuite/c-c++-common/attr-fallthrough-1.c create mode 100644 gcc/testsuite/c-c++-common/attr-fallthrough-2.c create mode 100644 gcc/testsuite/g++.dg/cpp0x/fallthrough1.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/fallthrough2.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/fallthrough1.C create mode 100644 gcc/testsuite/gcc.dg/Wimplicit-fallthrough-1.c create mode 100644 gcc/testsuite/obj-c++.dg/Wimplicit-fallthrough-1.mm create mode 100644 gcc/testsuite/objc.dg/Wimplicit-fallthrough-1.m 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 + + 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 + + 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 * 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 + + 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 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, "% attribute specified multiple " + "times"); + /* No attribute-argument-clause shall be present. */ + else if (TREE_VALUE (t) != NULL_TREE) + warning (OPT_Wattributes, "% 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, 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 + + PR c/7652 + * c-decl.c (pop_scope): Add gcc_fallthrough. + +2016-09-26 Marek Polacek + + 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 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, - 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 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, + "% 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, "% attribute " + "not followed by %<;%>"); + } + else if (attrs != NULL_TREE) + warning_at (loc, OPT_Wattributes, "only attribute %" + " 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, + "% attribute not followed " + "by %<;%>"); + } + else if (attrs != NULL_TREE) + warning_at (loc, OPT_Wattributes, "only attribute %" + " 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 + + 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 + + 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 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, + "% 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, + "% 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::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 %"); 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, + "% is a C++17 feature;" + " use %"); + 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 % 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 % 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 + + 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 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 *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 *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 (stmt); + stmt = gimple_seq_last_stmt (gimple_bind_body (bind)); + return last_stmt_in_scope (stmt); + } + + case GIMPLE_TRY: + { + gtry *try_stmt = as_a (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 *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 (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 ; 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 (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 ; else goto ; + : + ; + goto ; + : + */ + 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 (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 + [...] + + 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 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 (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 (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 (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 (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 % 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 %"); +} + /* 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 + + 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 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 + . */ + +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 , 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 + . */ + +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 + . */ + +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 + Jakub Jelinek + + 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 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 + + PR c/7652 + * libsupc++/hash_bytes.cc: Add [[gnu::fallthrough]]. + 2016-09-25 François Dumont * 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(buf[2]) << 16; + [[gnu::fallthrough]]; case 2: hash ^= static_cast(buf[1]) << 8; + [[gnu::fallthrough]]; case 1: hash ^= static_cast(buf[0]); hash *= m; -- cgit v1.1