@@ -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 }
};
@@ -9836,6 +9839,19 @@ handle_designated_init_attribute (tree *node, tree name, tree, int,
return NULL_TREE;
}
+
+/* Handle a "fallthrough" attribute; arguments as in struct
+ attribute_spec.handler. */
+
+static tree
+handle_fallthrough_attribute (tree *, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ *no_add_attrs = true;
+ return NULL_TREE;
+}
+
/* Check for valid arguments being passed to a function with FNTYPE.
There are NARGS arguments in the array ARGARRAY. LOC should be used for
@@ -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;
@@ -1618,6 +1621,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
@@ -1749,6 +1754,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
{
if (auto_type_p)
error_at (here, "%<__auto_type%> in empty declaration");
+ else if (specs->typespec_kind == ctsk_none && specs->attrs
+ && is_attribute_p ("fallthrough",
+ get_attribute_name (specs->attrs)))
+ {
+ tree fn = build_call_expr_internal_loc (here, IFN_FALLTHROUGH,
+ void_type_node, 0);
+ add_stmt (fn);
+ }
else if (empty_ok)
shadow_tag (specs);
else
@@ -4967,6 +4980,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;
@@ -5013,6 +5031,10 @@ c_parser_label (c_parser *parser)
}
if (label)
{
+ if (TREE_CODE (label) == LABEL_EXPR)
+ FALLTHROUGH_LABEL_P (LABEL_EXPR_LABEL (label)) = fallthrough_p;
+ else
+ FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p;
if (c_parser_next_tokens_start_declaration (parser))
{
error_at (c_parser_peek_token (parser)->location,
@@ -5066,6 +5088,9 @@ c_parser_label (c_parser *parser)
jump-statement:
goto * expression ;
+ expression-statement:
+ attributes ;
+
Objective-C:
statement:
@@ -5327,6 +5352,27 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
gcc_assert (c_dialect_objc ());
c_parser_objc_synchronized_statement (parser);
break;
+ case RID_ATTRIBUTE:
+ {
+ /* Allow '__attribute__((fallthrough));'. */
+ tree attrs = c_parser_attributes (parser);
+ if (attrs != NULL_TREE
+ && is_attribute_p ("fallthrough", get_attribute_name (attrs)))
+ {
+ location_t loc = c_parser_peek_token (parser)->location;
+ c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>");
+ tree fn = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH,
+ void_type_node, 0);
+ add_stmt (fn);
+ }
+ else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+ c_parser_error (parser, "only attribute %<fallthrough%> "
+ "can be used");
+ else
+ c_parser_error (parser, "only attribute %<fallthrough%> "
+ "followed by %<;%> can be used");
+ }
+ break;
default:
goto expr_stmt;
}
@@ -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.
@@ -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:
@@ -4838,6 +4839,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:
@@ -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);
@@ -5135,6 +5135,31 @@ cp_parser_primary_expression (cp_parser *parser,
case RID_AT_SELECTOR:
return cp_parser_objc_expression (parser);
+ case RID_ATTRIBUTE:
+ {
+ /* This might be __attribute__((fallthrough));. */
+ tree attr = cp_parser_gnu_attributes_opt (parser);
+ if (attr != NULL_TREE
+ && is_attribute_p ("fallthrough", get_attribute_name (attr)))
+ {
+ tree fn = build_call_expr_internal_loc (token->location,
+ IFN_FALLTHROUGH,
+ void_type_node, 0);
+ return cp_expr (fn, token->location);
+ }
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+ {
+ cp_parser_error (parser, "only attribute %<fallthrough%> "
+ "can be used");
+ return error_mark_node;
+ }
+ else
+ {
+ cp_parser_error (parser, "expected primary-expression");
+ return error_mark_node;
+ }
+ }
+
case RID_TEMPLATE:
if (parser->in_function_body
&& (cp_lexer_peek_nth_token (parser->lexer, 2)->type
@@ -10585,15 +10610,26 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
}
/* Look for an expression-statement instead. */
statement = cp_parser_expression_statement (parser, in_statement_expr);
+
+ /* Handle [[fallthrough]];. */
+ if (std_attrs != NULL_TREE
+ && is_attribute_p ("fallthrough", get_attribute_name (std_attrs))
+ && statement == NULL_TREE)
+ {
+ tree fn = build_call_expr_internal_loc (statement_location,
+ IFN_FALLTHROUGH,
+ void_type_node, 0);
+ finish_expr_stmt (fn);
+ }
}
/* Set the line number for the statement. */
if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
SET_EXPR_LOCATION (statement, statement_location);
- /* Note that for now, we don't do anything with c++11 statements
- parsed at this level. */
- if (std_attrs != NULL_TREE)
+ /* Allow "[[fallthrough]];", but warn otherwise. */
+ if (std_attrs != NULL_TREE
+ && !is_attribute_p ("fallthrough", get_attribute_name (std_attrs)))
warning_at (attrs_location,
OPT_Wattributes,
"attributes at the beginning of statement are ignored");
@@ -10628,6 +10664,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 +10699,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 +10716,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 +10728,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;
}
@@ -24114,6 +24164,16 @@ cp_parser_std_attribute (cp_parser *parser)
" use %<gnu::deprecated%>");
TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
}
+ /* C++17 fallthrough attribute is equivalent to GNU's. */
+ else if (cxx_dialect >= cxx11
+ && is_attribute_p ("fallthrough", attr_id))
+ {
+ if (cxx_dialect < cxx1z)
+ pedwarn (token->location, OPT_Wpedantic,
+ "%<fallthrough%> is a C++17 feature;"
+ " use %<gnu::fallthrough%>");
+ TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
+ }
/* Transactional Memory TS optimize_for_synchronized attribute is
equivalent to GNU transaction_callable. */
else if (is_attribute_p ("optimize_for_synchronized", attr_id))
@@ -24172,11 +24232,15 @@ cp_parser_check_std_attribute (tree attributes, tree attribute)
tree name = get_attribute_name (attribute);
if (is_attribute_p ("noreturn", name)
&& lookup_attribute ("noreturn", attributes))
- error ("attribute noreturn can appear at most once "
+ error ("attribute %<noreturn%> can appear at most once "
"in an attribute-list");
else if (is_attribute_p ("deprecated", name)
&& lookup_attribute ("deprecated", attributes))
- error ("attribute deprecated can appear at most once "
+ error ("attribute %<deprecated%> can appear at most once "
+ "in an attribute-list");
+ else if (is_attribute_p ("fallthrough", name)
+ && lookup_attribute ("fallthrough", attributes))
+ error ("attribute %<fallthrough%> can appear at most once "
"in an attribute-list");
}
}
@@ -16556,7 +16556,9 @@ 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)
+ 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 +22789,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. */
@@ -60,6 +60,7 @@ extensions, accepted by GCC in C90 mode and in C++.
* Type Attributes:: Specifying attributes of types.
* Label Attributes:: Specifying attributes on labels.
* Enumerator Attributes:: Specifying attributes on enumerators.
+* Statement Attributes:: Specifying attributes on statements.
* Attribute Syntax:: Formal syntax for attributes.
* Function Prototypes:: Prototype declarations and old-style definitions.
* C++ Comments:: C++ comments are recognized.
@@ -2261,6 +2262,7 @@ GCC also supports attributes on
variable declarations (@pxref{Variable Attributes}),
labels (@pxref{Label Attributes}),
enumerators (@pxref{Enumerator Attributes}),
+statements (@pxref{Statement Attributes}),
and types (@pxref{Type Attributes}).
There is some overlap between the purposes of attributes and pragmas
@@ -5563,8 +5565,8 @@ attributes are currently defined generically for variables.
Other attributes are defined for variables on particular target
systems. Other attributes are available for functions
(@pxref{Function Attributes}), labels (@pxref{Label Attributes}),
-enumerators (@pxref{Enumerator Attributes}), and for types
-(@pxref{Type Attributes}).
+enumerators (@pxref{Enumerator Attributes}), statements
+(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}).
Other front ends might define more attributes
(@pxref{C++ Extensions,,Extensions to the C++ Language}).
@@ -6345,7 +6347,8 @@ attributes of types. Some type attributes apply only to @code{struct}
and @code{union} types, while others can apply to any type defined
via a @code{typedef} declaration. Other attributes are defined for
functions (@pxref{Function Attributes}), labels (@pxref{Label
-Attributes}), enumerators (@pxref{Enumerator Attributes}), and for
+Attributes}), enumerators (@pxref{Enumerator Attributes}),
+statements (@pxref{Statement Attributes}), and for
variables (@pxref{Variable Attributes}).
The @code{__attribute__} keyword is followed by an attribute specification
@@ -6855,7 +6858,8 @@ GCC allows attributes to be set on C labels. @xref{Attribute Syntax}, for
details of the exact syntax for using attributes. Other attributes are
available for functions (@pxref{Function Attributes}), variables
(@pxref{Variable Attributes}), enumerators (@pxref{Enumerator Attributes}),
-and for types (@pxref{Type Attributes}).
+statements (@pxref{Statement Attributes}), and for types
+(@pxref{Type Attributes}).
This example uses the @code{cold} label attribute to indicate the
@code{ErrorHandling} branch is unlikely to be taken and that the
@@ -6908,8 +6912,8 @@ with computed goto or @code{asm goto}.
GCC allows attributes to be set on enumerators. @xref{Attribute Syntax}, for
details of the exact syntax for using attributes. Other attributes are
available for functions (@pxref{Function Attributes}), variables
-(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}),
-and for types (@pxref{Type Attributes}).
+(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), statements
+(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}).
This example uses the @code{deprecated} enumerator attribute to indicate the
@code{oldval} enumerator is deprecated:
@@ -6940,6 +6944,46 @@ do instead. Note that the warnings only occurs for uses.
@end table
+@node Statement Attributes
+@section Statement Attributes
+@cindex Statement Attributes
+
+GCC allows attributes to be set on null statements. @xref{Attribute Syntax},
+for details of the exact syntax for using attributes. Other attributes are
+available for functions (@pxref{Function Attributes}), variables
+(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators
+(@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}).
+
+This example uses the @code{fallthrough} statement attribute to indicate that
+the @option{-Wimplicit-fallthrough} warning should not be emitted:
+
+@smallexample
+switch (cond)
+ @{
+ case 1:
+ bar (1);
+ __attribute__((fallthrough));
+ case 2:
+ @dots{}
+ @}
+@end smallexample
+
+@table @code
+@item fallthrough
+@cindex @code{fallthrough} statement attribute
+The @code{fallthrough} attribute with a null statement serves as a
+fallthrough statement. It hints to the compiler that a statement
+that falls through to another case label, or user-defined label
+in a switch statement is intentional and thus the
+@option{-Wimplicit-fallthrough} warning must not trigger. The
+fallthrough attribute may appear at most once in each attribute
+list, and may not be mixed with other attributes. It can only
+be used in a switch statement (the compiler will issue an error
+otherwise), after a preceding statement and before a logically
+succeeding case label, or user-defined label.
+
+@end table
+
@node Attribute Syntax
@section Attribute Syntax
@cindex attribute syntax
@@ -6967,6 +7011,8 @@ and enumerated types.
applying to labels.
@xref{Enumerator Attributes}, for details of the semantics of attributes
applying to enumerators.
+@xref{Statement Attributes}, for details of the semantics of attributes
+applying to statements.
An @dfn{attribute specifier} is of the form
@code{__attribute__ ((@var{attribute-list}))}. An @dfn{attribute list}
@@ -7032,6 +7078,10 @@ present. The optional attribute in the enumerator appertains to the
enumeration constant. It is not possible to place the attribute after
the constant expression, if present.
+@subsubheading Statement Attributes
+In GNU C, an attribute specifier list may appear as part of a null
+statement. The attribute goes before the semicolon.
+
@subsubheading Type Attributes
An attribute specifier list may appear as part of a @code{struct},
@@ -272,8 +272,8 @@ Objective-C and Objective-C++ Dialects}.
-Wformat-security -Wformat-signedness -Wformat-y2k -Wframe-address @gol
-Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol
-Wignored-qualifiers -Wignored-attributes -Wincompatible-pointer-types @gol
--Wimplicit -Wimplicit-function-declaration -Wimplicit-int @gol
--Winit-self -Winline -Wno-int-conversion @gol
+-Wimplicit -Wimplicit-fallthrough -Wimplicit-function-declaration @gol
+-Wimplicit-int -Winit-self -Winline -Wno-int-conversion @gol
-Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol
-Winvalid-pch -Wlarger-than=@var{len} @gol
-Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
@@ -3667,6 +3667,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
@@ -3953,6 +3954,91 @@ enabled by default and it is made into an error by
Same as @option{-Wimplicit-int} and @option{-Wimplicit-function-declaration}.
This warning is enabled by @option{-Wall}.
+@item -Wimplicit-fallthrough
+@opindex Wimplicit-fallthrough
+@opindex Wno-implicit-fallthrough
+Warn when a switch case falls through. For example:
+
+@smallexample
+@group
+switch (cond)
+ @{
+ case 1:
+ a = 1;
+ break;
+ case 2:
+ a = 2;
+ case 3:
+ a = 3;
+ break;
+ @}
+@end group
+@end smallexample
+
+This warning does not warn when the last statement of a case cannot
+fall through, e.g. when there is a return statement of a function
+declared with the noreturn attribute. @option{-Wimplicit-fallthrough}
+also takes into account control flow statements, such as ifs, and only
+warns when appropriate. E.g.@:
+
+@smallexample
+@group
+switch (cond)
+ @{
+ case 1:
+ if (i > 3) @{
+ bar (5);
+ break;
+ @} else if (i < 1) @{
+ bar (0);
+ @} else
+ return;
+ default:
+ @dots{}
+ @}
+@end group
+@end smallexample
+
+Since there are occasions where a switch case fall through is desirable,
+GCC provides an attribute, @code{__attribute__ ((fallthrough))}, that is
+to be used along with a null statement to suppress this warning that
+would normally occur:
+
+@smallexample
+@group
+switch (cond)
+ @{
+ case 1:
+ bar (0);
+ __attribute__ ((fallthrough));
+ default:
+ @dots{}
+ @}
+@end group
+@end smallexample
+
+C++17 provides a standard way to suppress the @option{-Wimplicit-fallthrough}
+warning using @code{[[fallthrough]];} instead of the GNU attribute. In C++11
+or C++14 users can use @code{[[gnu::fallthrough]];}, which is a GNU extension.
+Instead of the these attributes, it is also possible to add a "falls through"
+comment to silence the warning. GCC accepts a wide range of such comments,
+for example all of "Falls through.", "fallthru", "FALLS-THROUGH" work.
+
+@smallexample
+@group
+switch (cond)
+ @{
+ case 1:
+ bar (0);
+ /* FALLTHRU */
+ default:
+ @dots{}
+ @}
+@end group
+@end smallexample
+
+This warning is enabled by @option{-Wextra}.
+
@item -Wignored-qualifiers @r{(C and C++ only)}
@opindex Wignored-qualifiers
@opindex Wno-ignored-qualifiers
@@ -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. */
@@ -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,413 @@ maybe_warn_switch_unreachable (gimple_seq seq)
}
}
+
+/* A label entry that pairs label and a location. */
+struct label_entry
+{
+ tree label;
+ location_t loc;
+};
+
+/* Find LABEL in vector of label entries VEC. */
+
+static struct label_entry *
+find_label_entry (const auto_vec<struct label_entry> *vec, tree label)
+{
+ unsigned int i;
+ struct label_entry *l;
+
+ FOR_EACH_VEC_ELT (*vec, i, l)
+ if (l->label == label)
+ return l;
+ return NULL;
+}
+
+/* Return true if LABEL, a LABEL_DECL, represents a case label
+ in a vector of labels CASES. */
+
+static bool
+case_label_p (const vec<tree> *cases, tree label)
+{
+ unsigned int i;
+ tree l;
+
+ FOR_EACH_VEC_ELT (*cases, i, l)
+ if (CASE_LABEL (l) == label)
+ return true;
+ return false;
+}
+
+/* Find the last statement in a scope STMT. */
+
+static gimple *
+last_stmt_in_scope (gimple *stmt)
+{
+ if (!stmt)
+ return NULL;
+
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_BIND:
+ {
+ gbind *bind = as_a <gbind *> (stmt);
+ stmt = gimple_seq_last_stmt (gimple_bind_body (bind));
+ return last_stmt_in_scope (stmt);
+ }
+
+ case GIMPLE_TRY:
+ {
+ gtry *try_stmt = as_a <gtry *> (stmt);
+ stmt = gimple_seq_last_stmt (gimple_try_eval (try_stmt));
+ return last_stmt_in_scope (stmt);
+ }
+
+ default:
+ return stmt;
+ }
+}
+
+/* Collect interesting labels in LABELS and return the statement preceding
+ another case label, or a user-defined label. */
+
+static gimple *
+collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
+ auto_vec <struct label_entry> *labels)
+{
+ gimple *prev = NULL;
+
+ do
+ {
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND
+ || gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY)
+ {
+ /* Nested scope. Only look at the last statement of
+ the innermost scope. */
+ location_t bind_loc = gimple_location (gsi_stmt (*gsi_p));
+ gimple *last = last_stmt_in_scope (gsi_stmt (*gsi_p));
+ if (last)
+ {
+ prev = last;
+ /* It might be a label without a location. Use the
+ location of the scope then. */
+ if (!gimple_has_location (prev))
+ gimple_set_location (prev, bind_loc);
+ }
+ gsi_next (gsi_p);
+ continue;
+ }
+
+ /* If's are tricky. */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_COND)
+ {
+ gcond *cond_stmt = as_a <gcond *> (gsi_stmt (*gsi_p));
+ tree false_lab = gimple_cond_false_label (cond_stmt);
+ location_t if_loc = gimple_location (cond_stmt);
+
+ /* If we have e.g.
+ if (i > 1) goto <D.2259>; else goto D;
+ we can't do much with the else-branch. */
+ if (!DECL_ARTIFICIAL (false_lab))
+ break;
+
+ /* Go on until the false label, then one step back. */
+ for (; !gsi_end_p (*gsi_p); gsi_next (gsi_p))
+ {
+ gimple *stmt = gsi_stmt (*gsi_p);
+ if (gimple_code (stmt) == GIMPLE_LABEL
+ && gimple_label_label (as_a <glabel *> (stmt)) == false_lab)
+ break;
+ }
+
+ /* Not found? Oops. */
+ if (gsi_end_p (*gsi_p))
+ break;
+
+ struct label_entry l = { false_lab, if_loc };
+ labels->safe_push (l);
+
+ /* Go to the last statement of the then branch. */
+ gsi_prev (gsi_p);
+
+ /* if (i != 0) goto <D.1759>; else goto <D.1760>;
+ <D.1759>:
+ <stmt>;
+ goto <D.1761>;
+ <D.1760>:
+ */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+ && !gimple_has_location (gsi_stmt (*gsi_p)))
+ {
+ /* Look at the statement before, it might be
+ __attribute__((fallthrough));, in which case don't warn. */
+ gsi_prev (gsi_p);
+ bool fallthru_before_dest
+ = gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_FALLTHROUGH);
+ gsi_next (gsi_p);
+ tree goto_dest = gimple_goto_dest (gsi_stmt (*gsi_p));
+ if (!fallthru_before_dest)
+ {
+ struct label_entry l = { goto_dest, if_loc };
+ labels->safe_push (l);
+ }
+ }
+ /* And move back. */
+ gsi_next (gsi_p);
+ }
+
+ /* Remember the last statement. Skip labels that are of no interest
+ to us. */
+ if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+ {
+ tree label = gimple_label_label (as_a <glabel *> (gsi_stmt (*gsi_p)));
+ if (find_label_entry (labels, label))
+ prev = gsi_stmt (*gsi_p);
+ }
+ else
+ prev = gsi_stmt (*gsi_p);
+ gsi_next (gsi_p);
+ }
+ while (!gsi_end_p (*gsi_p)
+ /* Stop if we find a case or a user-defined label. */
+ && (gimple_code (gsi_stmt (*gsi_p)) != GIMPLE_LABEL
+ || !gimple_has_location (gsi_stmt (*gsi_p))));
+
+ return prev;
+}
+
+/* Return true if the switch fallthough warning should occur. LABEL is
+ the label statement that we're falling through to. */
+
+static bool
+should_warn_for_implicit_fallthrough (gimple_stmt_iterator *gsi_p, tree label)
+{
+ gimple_stmt_iterator gsi = *gsi_p;
+
+ /* Don't warn for a non-case label followed by a statement:
+ case 0:
+ foo ();
+ label:
+ bar ();
+ as these are likely intentional. */
+ if (!case_label_p (&gimplify_ctxp->case_labels, label))
+ {
+ gsi_next (&gsi);
+ if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
+ return false;
+ }
+
+ /* Don't warn for terminated branches, i.e. when the subsequent case labels
+ immediately breaks. */
+ gsi = *gsi_p;
+
+ /* Skip all immediately following labels. */
+ while (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+ gsi_next (&gsi);
+
+ /* { ... something; default:; } */
+ if (gsi_end_p (gsi)
+ /* { ... something; default: break; } or
+ { ... something; default: goto L; } */
+ || gimple_code (gsi_stmt (gsi)) == GIMPLE_GOTO
+ /* { ... something; default: return; } */
+ || gimple_code (gsi_stmt (gsi)) == GIMPLE_RETURN)
+ return false;
+
+ return true;
+}
+
+/* Callback for walk_gimple_seq. */
+
+static tree
+warn_implicit_fallthrough_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
+ struct walk_stmt_info *)
+{
+ gimple *stmt = gsi_stmt (*gsi_p);
+
+ *handled_ops_p = true;
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_TRY:
+ case GIMPLE_BIND:
+ case GIMPLE_CATCH:
+ case GIMPLE_EH_FILTER:
+ case GIMPLE_TRANSACTION:
+ /* Walk the sub-statements. */
+ *handled_ops_p = false;
+ break;
+
+ /* Find a sequence of form:
+
+ GIMPLE_LABEL
+ [...]
+ <may fallthru stmt>
+ GIMPLE_LABEL
+
+ and possibly warn. */
+ case GIMPLE_LABEL:
+ {
+ /* Found a label. Skip all immediately following labels. */
+ while (!gsi_end_p (*gsi_p)
+ && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL)
+ gsi_next (gsi_p);
+
+ /* There might be no more statements. */
+ if (gsi_end_p (*gsi_p))
+ return integer_zero_node;
+
+ /* Vector of labels that fall through. */
+ auto_vec <struct label_entry> labels;
+ gimple *prev = collect_fallthrough_labels (gsi_p, &labels);
+
+ /* There might be no more statements. */
+ if (gsi_end_p (*gsi_p))
+ return integer_zero_node;
+
+ gimple *next = gsi_stmt (*gsi_p);
+ tree label;
+ /* If what follows is a label, then we may have a fallthrough. */
+ if (gimple_code (next) == GIMPLE_LABEL
+ && gimple_has_location (next)
+ && (label = gimple_label_label (as_a <glabel *> (next)))
+ && !FALLTHROUGH_LABEL_P (label)
+ && prev != NULL)
+ {
+ struct label_entry *l;
+ bool warned_p = false;
+ if (!should_warn_for_implicit_fallthrough (gsi_p, label))
+ /* Quiet. */;
+ else if (gimple_code (prev) == GIMPLE_LABEL
+ && (label = gimple_label_label (as_a <glabel *> (prev)))
+ && (l = find_label_entry (&labels, label)))
+ warned_p = warning_at (l->loc, OPT_Wimplicit_fallthrough,
+ "this statement may fall through");
+ else if (!gimple_call_internal_p (prev, IFN_FALLTHROUGH)
+ /* Try to be clever and don't warn when the statement
+ can't actually fall through. */
+ && gimple_stmt_may_fallthru (prev)
+ && gimple_has_location (prev))
+ warned_p = warning_at (gimple_location (prev),
+ OPT_Wimplicit_fallthrough,
+ "this statement may fall through");
+ if (warned_p)
+ {
+ /* Suggestion one: add attribute fallthrough. */
+ rich_location rloc_attr (line_table, gimple_location (next));
+ /* For C++17, we'd recommend [[fallthrough]];, but we're not
+ there yet. For C++11, recommend [[gnu::fallthrough]];. */
+ if (strncmp (lang_hooks.name, "GNU C++14", 9) == 0
+ || strncmp (lang_hooks.name, "GNU C++11", 9) == 0)
+ {
+ rloc_attr.add_fixit_insert (gimple_location (next),
+ "[[gnu::fallthrough]];");
+ inform_at_rich_loc (&rloc_attr,
+ "insert %qs to silence this warning",
+ "[[gnu::fallthrough]];");
+ }
+ else
+ {
+ /* Otherwise, recommend __attribute__ ((fallthrough));. */
+ rloc_attr.add_fixit_insert (gimple_location (next),
+ "__attribute__ "
+ "((fallthrough));");
+ inform_at_rich_loc (&rloc_attr,
+ "insert %qs to silence this warning",
+ "__attribute__ ((fallthrough));");
+ }
+ /* Suggestion two: add "break;". */
+ rich_location rloc_break (line_table, gimple_location (next));
+ rloc_break.add_fixit_insert (gimple_location (next), "break;");
+ inform_at_rich_loc (&rloc_break,
+ "insert %qs to avoid fall-through",
+ "break;");
+ }
+
+ /* 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;
+ else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL
+ || (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+ && !gimple_has_location (gsi_stmt (*gsi_p))))
+ /* This usage is OK. */;
+ else
+ warning_at (gimple_location (stmt), 0,
+ "%<__attribute__ ((fallthrough))%> not preceding "
+ "a label");
+ }
+ break;
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Expand all FALLTHROUGH () calls in SEQ. */
+
+static void
+expand_FALLTHROUGH (gimple_seq *seq_p)
+{
+ struct walk_stmt_info wi;
+ memset (&wi, 0, sizeof (wi));
+ walk_gimple_seq_mod (seq_p, expand_FALLTHROUGH_r, NULL, &wi);
+}
+
/* Gimplify a SWITCH_EXPR, and collect the vector of labels it can
branch to. */
@@ -1660,10 +2068,17 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
labels. Save all the things from the switch body to append after. */
saved_labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels.create (8);
+ bool old_in_switch_expr = gimplify_ctxp->in_switch_expr;
+ gimplify_ctxp->in_switch_expr = true;
gimplify_stmt (&SWITCH_BODY (switch_expr), &switch_body_seq);
+ gimplify_ctxp->in_switch_expr = old_in_switch_expr;
maybe_warn_switch_unreachable (switch_body_seq);
+ maybe_warn_implicit_fallthrough (switch_body_seq);
+ /* Only do this for the innermost GIMPLE_SWITCH. */
+ if (!gimplify_ctxp->in_switch_expr)
+ expand_FALLTHROUGH (&switch_body_seq);
labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels = saved_labels;
@@ -1694,6 +2109,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 +2141,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);
@@ -10746,11 +11177,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:
@@ -244,6 +244,15 @@ expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
gcc_unreachable ();
}
+/* This should get expanded in the lower pass. */
+
+static void
+expand_FALLTHROUGH (internal_fn, gcall *call)
+{
+ error_at (gimple_location (call),
+ "invalid use of attribute %<fallthrough%>");
+}
+
/* Helper function for expand_addsub_overflow. Return 1
if ARG interpreted as signed in its precision is known to be always
positive or 2 if ARG is known to be always negative, or 3 if ARG may
@@ -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
@@ -717,3 +717,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;
+}
@@ -537,5 +537,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 */
@@ -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
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
@@ -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);
+ }
+}
@@ -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:;
+ }
+}
@@ -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);
+ }
+}
@@ -0,0 +1,63 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+
+/* As per <http://security.coverity.com/blog/2013/Sep/gimme-a-break.html>, don't
+ warn for terminated branches (fall through to break / end of the switch). */
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ return;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ goto X;
+ }
+X:
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:
+ break;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ case 2:
+ case 3:
+ default:
+ break;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ default:;
+ }
+
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ case 2:
+ case 3:
+ default:;
+ }
+}
@@ -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:;
+ }
+}
@@ -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;
+ }
+ }
+}
@@ -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;
+ }
+}
@@ -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;
+ }
+}
@@ -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;
+}
@@ -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);
+ }
+ }
+}
@@ -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 ();
+ }
+}
@@ -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 ();
+ }
+}
@@ -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 ();
+ }
+}
@@ -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:;
+ }
+}
@@ -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);
+ }
+}
@@ -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:
+ {
+ int a[i]; /* { dg-warning "statement may fall through" } */
+ }
+ 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);
+ }
+}
@@ -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:;
+}
@@ -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:;
+ }
+}
@@ -0,0 +1,57 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic" } */
+
+extern void bar (int);
+void
+fn (int i)
+{
+ __attribute__((fallthrough)) int j = 0; /* { dg-warning "attribute ignored" } */
+
+ if (j)
+ __attribute__((fallthrough)); /* { dg-error "invalid use" } */
+
+ __attribute__((fallthrough)); /* { dg-error "invalid use" } */
+ switch (i)
+ {
+ __attribute__((fallthrough)); /* { dg-warning "statement will never" } */
+ case 1:
+ i++;
+ __attribute__((fallthrough));
+ case 2:
+ if (i) /* { dg-warning "statement may fall through" } */
+ bar (2);
+ else
+ __attribute__((fallthrough));
+ case 3:
+ if (i > 1)
+ __attribute__((fallthrough));
+ else
+ return;
+ case 4:
+ if (i)
+ __attribute__((fallthrough));
+ __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" } */
+}
@@ -0,0 +1,47 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra -Wpedantic -Wno-unused -Wno-implicit-fallthrough" } */
+
+extern void bar (int);
+void
+fn (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ __attribute__((used));
+ /* { dg-warning "empty declaration" "" { target c } 13 } */
+ /* { dg-error "only attribute .fallthrough." "" { target c++ } 13 } */
+ case 2:
+ bar (1);
+ __attribute__((foo));
+ /* { dg-error "only attribute .fallthrough." "" { target c++ } 18 } */
+ /* { dg-warning "empty declaration" "" { target c } 18 } */
+ case 3:
+ bar (1);
+ __attribute__((fallthrough))
+ /* { dg-warning "not preceding a label" "" { target c++ } 23 } */
+ case 4: /* { dg-error "expected" } */
+ bar (1);
+ __attribute__((fallthrough)) 1; /* { dg-error "expected" } */
+ case 5:
+ bar (1);
+ __attribute__((fallthrough)) int i; /* { dg-warning "ignored" } */
+ case 6:
+ bar (1);
+ __attribute__((fallthrough ("x")));
+ case 7:
+ bar (1);
+ __attribute__((fallthrough, fallthrough));
+ case 8:
+ bar (1);
+ __attribute__((fallthrough));
+ case 9:
+ __attribute__((fallthrough)); /* { dg-error "label" "" { target c } } */
+ /* { dg-warning "not preceding a label" "" { target *-*-* } 41 } */
+ bar (1);
+ default:
+ bar (99);
+ }
+}
@@ -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]];
+ [[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" }
+}
@@ -0,0 +1,21 @@
+// PR c/7652
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wextra -Wall -Wpedantic" }
+
+extern void bar (int);
+
+void
+f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ bar (1);
+ [[fallthrough]]; // { dg-warning ".fallthrough. is a C\\+\\+17 feature" }
+ case 3:
+ bar (1);
+ [[gnu::fallthrough, gnu::fallthrough]]; // { dg-error ".fallthrough. can appear at most once" }
+ case 2:
+ bar (2);
+ }
+}
@@ -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);
+ }
+}
@@ -0,0 +1,33 @@
+/* PR c/7652 */
+/* { dg-do compile { target { ! c++11 } } } */
+/* { dg-options "-Wimplicit-fallthrough -fdiagnostics-show-caret" } */
+
+/* Test fixit notes. */
+
+extern int bar (int);
+
+void
+fn (int i)
+{
+ switch (i)
+ {
+ case 0:
+ bar (0);
+/* { dg-warning "this statement may fall through" "" { target *-*-* } 15 } */
+/*
+{ dg-begin-multiline-output "" }
+ bar (0);
+ ^
+ case 1:
+ ^~~~
+ __attribute__ ((fallthrough));
+ case 1:
+ ^~~~
+ break;
+{ dg-end-multiline-output "" }
+*/
+ case 1:
+ bar (1);
+ break;
+ }
+}
@@ -0,0 +1,33 @@
+/* PR c/7652 */
+/* { dg-do compile { target { c++11 } } } */
+/* { dg-options "-Wimplicit-fallthrough -fdiagnostics-show-caret" } */
+
+/* Test fixit notes. */
+
+extern int bar (int);
+
+void
+fn (int i)
+{
+ switch (i)
+ {
+ case 0:
+ bar (0);
+/* { dg-warning "this statement may fall through" "" { target *-*-* } 15 } */
+/*
+{ dg-begin-multiline-output "" }
+ bar (0);
+ ^
+ case 1:
+ ^~~~
+ [[gnu::fallthrough]];
+ case 1:
+ ^~~~
+ break;
+{ dg-end-multiline-output "" }
+*/
+ case 1:
+ bar (1);
+ break;
+ }
+}
@@ -0,0 +1,33 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough -fdiagnostics-show-caret" } */
+
+/* Test fixit notes. */
+
+extern int bar (int);
+
+void
+fn (int i)
+{
+ switch (i)
+ {
+ case 0:
+ bar (0);
+/* { dg-warning "this statement may fall through" "" { target *-*-* } 15 } */
+/*
+{ dg-begin-multiline-output "" }
+ bar (0);
+ ^~~~~~~
+ case 1:
+ ^~~~
+ __attribute__ ((fallthrough));
+ case 1:
+ ^~~~
+ break;
+{ dg-end-multiline-output "" }
+*/
+ case 1:
+ bar (1);
+ break;
+ }
+}
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
@@ -0,0 +1,38 @@
+/* PR c/7652 */
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-fallthrough" } */
+/* Test taken from
+ <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0188r0.pdf>. */
+
+extern void f (int);
+
+void
+foo (int n)
+{
+ switch (n)
+ {
+ case 22:
+ case 33:
+ f (1); /* { dg-warning "statement may fall through" } */
+ case 44:
+ f (2);
+ __attribute__((fallthrough));
+ case 55:
+ if (n > 10)
+ {
+ f (3);
+ break;
+ }
+ else
+ {
+ f (4);
+ __attribute__((fallthrough));
+ }
+ case 66:
+ f (5);
+ __attribute__((fallthrough)); /* { dg-warning "not preceding" } */
+ f (6); /* { dg-warning "statement may fall through" } */
+ case 77:
+ f (7);
+ }
+}
@@ -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
@@ -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.
@@ -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. */
@@ -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;