On Fri, Jul 22, 2016 at 01:06:41PM +0200, Jakub Jelinek wrote: > On Fri, Jul 22, 2016 at 12:44:07PM +0200, Marek Polacek wrote: > > --- gcc/gcc/cp/parser.h > > +++ gcc/gcc/cp/parser.h > > @@ -46,7 +46,7 @@ struct GTY (()) cp_token { > > Otherwise, this value is RID_MAX. */ > > ENUM_BITFIELD (rid) keyword : 8; > > /* Token flags. */ > > - unsigned char flags; > > + unsigned short flags; > > /* True if this token is from a context where it is implicitly extern > > "C" */ > > BOOL_BITFIELD implicit_extern_c : 1; > > /* True if an error has already been reported for this token, such as a > > I'm afraid this is really bad. > Right now, there are 8 and 8 bit bitfields, then 8-bit char, 3 individual > bits, 5 unused bits and 32-bit int, nicely packed into 64-bit word before a > union with pointer members, and the C++ FE lexes everything first, so there > are possibly millions of tokens in memory. > Can't you just make it unsigned int flags : 11; instead? Or instead > reshuffle the cpplib.h flags? E.g. I don't see the C++ FE to use the > NO_EXPAND flag, so moving it to the upper byte of the short and moving the > new flag to its bit? Perhaps that is even better for now.
Ouch, sorry, that is embarassing. I had been meaning to get back to this, but apparently I never did. This version should be better. Another change is that we don't warn for { something; default: return; } even in nested switches. Another change is that I made the FEs less permissive wrt invalid code. CCing Jason and Joseph to see what they think of the FEs part. 2016-07-27 Marek Polacek <pola...@redhat.com> Jakub Jelinek <ja...@redhat.com> PR c/7652 gcc/ * common.opt (Wimplicit-fallthrough): New option. * doc/extend.texi: Document statement attributes and the fallthrough attribute. * doc/invoke.texi: Document -Wimplicit-fallthrough. * gimple.h (gimple_call_internal_p): New function. * gimplify.c (struct gimplify_ctx): Add in_switch_expr. (struct label_entry): New struct. (find_label_entry): New function. (last_stmt_in_scope): New function. (warn_implicit_fallthrough_r): New function. (maybe_warn_implicit_fallthrough): New function. (expand_FALLTHROUGH_r): New function. (expand_FALLTHROUGH): New function. (gimplify_switch_expr): Call maybe_warn_implicit_fallthrough and expand_FALLTHROUGH for the innermost GIMPLE_SWITCH. (gimplify_label_expr): New function. (gimplify_case_label_expr): Set location. (gimplify_expr): Call gimplify_label_expr. * internal-fn.c (expand_FALLTHROUGH): New function. * internal-fn.def (FALLTHROUGH): New internal function. * system.h (gcc_fallthrough): Define. * tree-core.h: Add FALLTHROUGH_LABEL_P comment. * tree.h (FALLTHROUGH_LABEL_P): Define. gcc/c-family/ * c-common.c (c_common_attribute_table): Add fallthrough attribute. (handle_fallthrough_attribute): New function. gcc/c/ * c-parser.c (struct c_token): Add flags field. (c_lex_one_token): Pass it to c_lex_with_flags. (c_parser_declaration_or_fndef): Turn __attribute__((fallthrough)); into IFN_FALLTHROUGH. (c_parser_label): Set FALLTHROUGH_LABEL_P on labels. (c_parser_statement_after_labels): Handle RID_ATTRIBUTE. gcc/cp/ * constexpr.c (cxx_eval_internal_function): Handle IFN_FALLTHROUGH. (potential_constant_expression_1): Likewise. * parser.c (cp_parser_primary_expression): Handle RID_ATTRIBUTE. (cp_parser_statement): Handle fallthrough attribute. (cp_parser_label_for_labeled_statement): Set FALLTHROUGH_LABEL_P on labels. (cp_parser_std_attribute): Handle fallthrough attribute. (cp_parser_check_std_attribute): Detect duplicated fallthrough attribute. gcc/testsuite/ * c-c++-common/Wimplicit-fallthrough-1.c: New test. * c-c++-common/Wimplicit-fallthrough-10.c: New test. * c-c++-common/Wimplicit-fallthrough-11.c: New test. * c-c++-common/Wimplicit-fallthrough-12.c: New test. * c-c++-common/Wimplicit-fallthrough-13.c: New test. * c-c++-common/Wimplicit-fallthrough-14.c: New test. * c-c++-common/Wimplicit-fallthrough-15.c: New test. * c-c++-common/Wimplicit-fallthrough-16.c: New test. * c-c++-common/Wimplicit-fallthrough-17.c: New test. * c-c++-common/Wimplicit-fallthrough-2.c: New test. * c-c++-common/Wimplicit-fallthrough-3.c: New test. * c-c++-common/Wimplicit-fallthrough-4.c: New test. * c-c++-common/Wimplicit-fallthrough-5.c: New test. * c-c++-common/Wimplicit-fallthrough-6.c: New test. * c-c++-common/Wimplicit-fallthrough-7.c: New test. * c-c++-common/Wimplicit-fallthrough-8.c: New test. * c-c++-common/Wimplicit-fallthrough-9.c: New test. * c-c++-common/attr-fallthrough-1.c: New test. * c-c++-common/attr-fallthrough-2.c: New test. * g++.dg/cpp0x/fallthrough1.C: New test. * g++.dg/cpp0x/fallthrough2.C: New test. * g++.dg/cpp1z/fallthrough1.C: New test. libcpp/ * include/cpplib.h (PREV_FALLTHROUGH): Define. * internal.h (CPP_FALLTHRU): Define. * lex.c (fallthrough_comment_p): New function. (_cpp_lex_direct): Set PREV_FALLTHROUGH on tokens succeeding a falls through comment. diff --git gcc/gcc/c-family/c-common.c gcc/gcc/c-family/c-common.c index 1c5974a..e33021a 100644 --- gcc/gcc/c-family/c-common.c +++ gcc/gcc/c-family/c-common.c @@ -392,6 +392,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *); static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *); static tree handle_bnd_legacy (tree *, tree, tree, int, bool *); static tree handle_bnd_instrument (tree *, tree, tree, int, bool *); +static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *); static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT); @@ -833,6 +834,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_bnd_legacy, false }, { "bnd_instrument", 0, 0, true, false, false, handle_bnd_instrument, false }, + { "fallthrough", 0, 0, false, false, false, + handle_fallthrough_attribute, false }, { NULL, 0, 0, false, false, false, NULL, false } }; @@ -9701,6 +9704,19 @@ handle_designated_init_attribute (tree *node, tree name, tree, int, return NULL_TREE; } + +/* Handle a "fallthrough" attribute; arguments as in struct + attribute_spec.handler. */ + +static tree +handle_fallthrough_attribute (tree *, tree name, tree, int, + bool *no_add_attrs) +{ + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + return NULL_TREE; +} + /* Check for valid arguments being passed to a function with FNTYPE. There are NARGS arguments in the array ARGARRAY. LOC should be used for diff --git gcc/gcc/c/c-parser.c gcc/gcc/c/c-parser.c index 8952bca..d6a0dd1 100644 --- gcc/gcc/c/c-parser.c +++ gcc/gcc/c/c-parser.c @@ -193,6 +193,8 @@ struct GTY (()) c_token { location_t location; /* The value associated with this token, if any. */ tree value; + /* Token flags. */ + unsigned char flags; source_range get_range () const { @@ -270,7 +272,8 @@ c_lex_one_token (c_parser *parser, c_token *token) { timevar_push (TV_LEX); - token->type = c_lex_with_flags (&token->value, &token->location, NULL, + token->type = c_lex_with_flags (&token->value, &token->location, + &token->flags, (parser->lex_untranslated_string ? C_LEX_STRING_NO_TRANSLATE : 0)); token->id_kind = C_ID_NONE; @@ -1607,6 +1610,8 @@ static void c_finish_oacc_routine (c_parser *, tree, tree, bool, bool, bool); declaration-specifiers declarator declaration-list[opt] compound-statement + attribute ; + Objective-C: attributes objc-class-definition attributes objc-category-definition @@ -1738,6 +1743,19 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, { if (auto_type_p) error_at (here, "%<__auto_type%> in empty declaration"); + else if (specs->typespec_kind == ctsk_none && specs->attrs) + { + if (is_attribute_p ("fallthrough", + get_attribute_name (specs->attrs))) + { + tree fn = build_call_expr_internal_loc (here, IFN_FALLTHROUGH, + void_type_node, 0); + add_stmt (fn); + } + else + error_at (here, "only attribute %<fallthrough%> can be used " + "before %<;%>"); + } else if (empty_ok) shadow_tag (specs); else @@ -4960,6 +4978,11 @@ c_parser_label (c_parser *parser) { location_t loc1 = c_parser_peek_token (parser)->location; tree label = NULL_TREE; + + /* Remember whether this case or a user-defined label is allowed to fall + through to. */ + bool fallthrough_p = c_parser_peek_token (parser)->flags & PREV_FALLTHROUGH; + if (c_parser_next_token_is_keyword (parser, RID_CASE)) { tree exp1, exp2; @@ -5006,6 +5029,10 @@ c_parser_label (c_parser *parser) } if (label) { + if (TREE_CODE (label) == LABEL_EXPR) + FALLTHROUGH_LABEL_P (LABEL_EXPR_LABEL (label)) = fallthrough_p; + else + FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p; if (c_parser_next_tokens_start_declaration (parser)) { error_at (c_parser_peek_token (parser)->location, @@ -5059,6 +5086,9 @@ c_parser_label (c_parser *parser) jump-statement: goto * expression ; + expression-statement: + attributes ; + Objective-C: statement: @@ -5320,6 +5350,27 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, gcc_assert (c_dialect_objc ()); c_parser_objc_synchronized_statement (parser); break; + case RID_ATTRIBUTE: + { + /* Allow '__attribute__((fallthrough));'. */ + tree attrs = c_parser_attributes (parser); + if (attrs != NULL_TREE + && is_attribute_p ("fallthrough", get_attribute_name (attrs))) + { + location_t loc = c_parser_peek_token (parser)->location; + c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"); + tree fn = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH, + void_type_node, 0); + add_stmt (fn); + } + else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + c_parser_error (parser, "only attribute %<fallthrough%> " + "can be used"); + else + c_parser_error (parser, "only attribute %<fallthrough%> " + "followed by %<;%> can be used"); + } + break; default: goto expr_stmt; } diff --git gcc/gcc/common.opt gcc/gcc/common.opt index 8a292ed..7ba10cb 100644 --- gcc/gcc/common.opt +++ gcc/gcc/common.opt @@ -601,6 +601,10 @@ Whsa Common Var(warn_hsa) Init(1) Warning Warn when a function cannot be expanded to HSAIL. +Wimplicit-fallthrough +Common Var(warn_implicit_fallthrough) Warning EnabledBy(Wextra) +Warn when a switch case falls through. + Winline Common Var(warn_inline) Warning Warn when an inlined function cannot be inlined. diff --git gcc/gcc/cp/constexpr.c gcc/gcc/cp/constexpr.c index 6bcb41a..e93c636 100644 --- gcc/gcc/cp/constexpr.c +++ gcc/gcc/cp/constexpr.c @@ -1290,6 +1290,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, case IFN_UBSAN_NULL: case IFN_UBSAN_BOUNDS: case IFN_UBSAN_VPTR: + case IFN_FALLTHROUGH: return void_node; case IFN_ADD_OVERFLOW: @@ -4718,6 +4719,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, case IFN_UBSAN_NULL: case IFN_UBSAN_BOUNDS: case IFN_UBSAN_VPTR: + case IFN_FALLTHROUGH: return true; case IFN_ADD_OVERFLOW: diff --git gcc/gcc/cp/parser.c gcc/gcc/cp/parser.c index b71b9e5..84bc512 100644 --- gcc/gcc/cp/parser.c +++ gcc/gcc/cp/parser.c @@ -5129,6 +5129,31 @@ cp_parser_primary_expression (cp_parser *parser, case RID_AT_SELECTOR: return cp_parser_objc_expression (parser); + case RID_ATTRIBUTE: + { + /* This might be __attribute__((fallthrough));. */ + tree attr = cp_parser_gnu_attributes_opt (parser); + if (attr != NULL_TREE + && is_attribute_p ("fallthrough", get_attribute_name (attr))) + { + tree fn = build_call_expr_internal_loc (token->location, + IFN_FALLTHROUGH, + void_type_node, 0); + return cp_expr (fn, token->location); + } + else if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) + { + cp_parser_error (parser, "only attribute %<fallthrough%> " + "can be used"); + return error_mark_node; + } + else + { + cp_parser_error (parser, "expected primary-expression"); + return error_mark_node; + } + } + case RID_TEMPLATE: if (parser->in_function_body && (cp_lexer_peek_nth_token (parser->lexer, 2)->type @@ -10558,15 +10583,26 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, } /* Look for an expression-statement instead. */ statement = cp_parser_expression_statement (parser, in_statement_expr); + + /* Handle [[fallthrough]];. */ + if (std_attrs != NULL_TREE + && is_attribute_p ("fallthrough", get_attribute_name (std_attrs)) + && statement == NULL_TREE) + { + tree fn = build_call_expr_internal_loc (statement_location, + IFN_FALLTHROUGH, + void_type_node, 0); + finish_expr_stmt (fn); + } } /* Set the line number for the statement. */ if (statement && STATEMENT_CODE_P (TREE_CODE (statement))) SET_EXPR_LOCATION (statement, statement_location); - /* Note that for now, we don't do anything with c++11 statements - parsed at this level. */ - if (std_attrs != NULL_TREE) + /* Allow "[[fallthrough]];", but warn otherwise. */ + if (std_attrs != NULL_TREE + && !is_attribute_p ("fallthrough", get_attribute_name (std_attrs))) warning_at (attrs_location, OPT_Wattributes, "attributes at the beginning of statement are ignored"); @@ -10601,6 +10637,10 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) return; } + /* Remember whether this case or a user-defined label is allowed to fall + through to. */ + bool fallthrough_p = token->flags & PREV_FALLTHROUGH; + parser->colon_corrects_to_scope_p = false; switch (token->keyword) { @@ -10632,7 +10672,11 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) expr_hi = NULL_TREE; if (parser->in_switch_statement_p) - finish_case_label (token->location, expr, expr_hi); + { + tree l = finish_case_label (token->location, expr, expr_hi); + if (l && TREE_CODE (l) == CASE_LABEL_EXPR) + FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p; + } else error_at (token->location, "case label %qE not within a switch statement", @@ -10645,7 +10689,11 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) cp_lexer_consume_token (parser->lexer); if (parser->in_switch_statement_p) - finish_case_label (token->location, NULL_TREE, NULL_TREE); + { + tree l = finish_case_label (token->location, NULL_TREE, NULL_TREE); + if (l && TREE_CODE (l) == CASE_LABEL_EXPR) + FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p; + } else error_at (token->location, "case label not within a switch statement"); break; @@ -10653,6 +10701,8 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) default: /* Anything else must be an ordinary label. */ label = finish_label_stmt (cp_parser_identifier (parser)); + if (label && TREE_CODE (label) == LABEL_DECL) + FALLTHROUGH_LABEL_P (label) = fallthrough_p; break; } @@ -24024,6 +24074,16 @@ cp_parser_std_attribute (cp_parser *parser) " use %<gnu::deprecated%>"); TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu"); } + /* C++17 fallthrough attribute is equivalent to GNU's. */ + else if (cxx_dialect >= cxx11 + && is_attribute_p ("fallthrough", attr_id)) + { + if (cxx_dialect < cxx1z) + pedwarn (token->location, OPT_Wpedantic, + "%<fallthrough%> is a C++17 feature;" + " use %<gnu::fallthrough%>"); + TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu"); + } /* Transactional Memory TS optimize_for_synchronized attribute is equivalent to GNU transaction_callable. */ else if (is_attribute_p ("optimize_for_synchronized", attr_id)) @@ -24088,6 +24148,10 @@ cp_parser_check_std_attribute (tree attributes, tree attribute) && lookup_attribute ("deprecated", attributes)) error ("attribute deprecated can appear at most once " "in an attribute-list"); + else if (is_attribute_p ("fallthrough", name) + && lookup_attribute ("fallthrough", attributes)) + error ("attribute fallthrough can appear at most once " + "in an attribute-list"); } } diff --git gcc/gcc/doc/extend.texi gcc/gcc/doc/extend.texi index ad8898c..4960d57 100644 --- gcc/gcc/doc/extend.texi +++ gcc/gcc/doc/extend.texi @@ -60,6 +60,7 @@ extensions, accepted by GCC in C90 mode and in C++. * Type Attributes:: Specifying attributes of types. * Label Attributes:: Specifying attributes on labels. * Enumerator Attributes:: Specifying attributes on enumerators. +* Statement Attributes:: Specifying attributes on statements. * Attribute Syntax:: Formal syntax for attributes. * Function Prototypes:: Prototype declarations and old-style definitions. * C++ Comments:: C++ comments are recognized. @@ -2240,6 +2241,7 @@ GCC also supports attributes on variable declarations (@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators (@pxref{Enumerator Attributes}), +statements (@pxref{Statement Attributes}), and types (@pxref{Type Attributes}). There is some overlap between the purposes of attributes and pragmas @@ -5536,8 +5538,8 @@ attributes are currently defined generically for variables. Other attributes are defined for variables on particular target systems. Other attributes are available for functions (@pxref{Function Attributes}), labels (@pxref{Label Attributes}), -enumerators (@pxref{Enumerator Attributes}), and for types -(@pxref{Type Attributes}). +enumerators (@pxref{Enumerator Attributes}), statements +(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}). Other front ends might define more attributes (@pxref{C++ Extensions,,Extensions to the C++ Language}). @@ -6318,7 +6320,8 @@ attributes of types. Some type attributes apply only to @code{struct} and @code{union} types, while others can apply to any type defined via a @code{typedef} declaration. Other attributes are defined for functions (@pxref{Function Attributes}), labels (@pxref{Label -Attributes}), enumerators (@pxref{Enumerator Attributes}), and for +Attributes}), enumerators (@pxref{Enumerator Attributes}), +statements (@pxref{Statement Attributes}), and for variables (@pxref{Variable Attributes}). The @code{__attribute__} keyword is followed by an attribute specification @@ -6828,7 +6831,8 @@ GCC allows attributes to be set on C labels. @xref{Attribute Syntax}, for details of the exact syntax for using attributes. Other attributes are available for functions (@pxref{Function Attributes}), variables (@pxref{Variable Attributes}), enumerators (@pxref{Enumerator Attributes}), -and for types (@pxref{Type Attributes}). +statements (@pxref{Statement Attributes}), and for types +(@pxref{Type Attributes}). This example uses the @code{cold} label attribute to indicate the @code{ErrorHandling} branch is unlikely to be taken and that the @@ -6881,8 +6885,8 @@ with computed goto or @code{asm goto}. GCC allows attributes to be set on enumerators. @xref{Attribute Syntax}, for details of the exact syntax for using attributes. Other attributes are available for functions (@pxref{Function Attributes}), variables -(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), -and for types (@pxref{Type Attributes}). +(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), statements +(@pxref{Statement Attributes}), and for types (@pxref{Type Attributes}). This example uses the @code{deprecated} enumerator attribute to indicate the @code{oldval} enumerator is deprecated: @@ -6913,6 +6917,46 @@ do instead. Note that the warnings only occurs for uses. @end table +@node Statement Attributes +@section Statement Attributes +@cindex Statement Attributes + +GCC allows attributes to be set on null statements. @xref{Attribute Syntax}, +for details of the exact syntax for using attributes. Other attributes are +available for functions (@pxref{Function Attributes}), variables +(@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators +(@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}). + +This example uses the @code{fallthrough} statement attribute to indicate that +the @option{-Wimplicit-fallthrough} warning should not be emitted: + +@smallexample +switch (cond) + @{ + case 1: + bar (1); + __attribute__((fallthrough)); + case 2: + @dots{} + @} +@end smallexample + +@table @code +@item fallthrough +@cindex @code{fallthrough} statement attribute +The @code{fallthrough} attribute with a null statement serves as a +fallthrough statement. It hints to the compiler that a statement +that falls through to another case label, or user-defined label +in a switch statement is intentional and thus the +@option{-Wimplicit-fallthrough} warning must not trigger. The +fallthrough attribute might appear at most once in each attribute +list, and might not be mixed with other attributes. It can only +be used in a switch statement (the compiler will issue an error +otherwise), after a preceding statement and before a logically +succeeding case label, or user-defined label. + +@end table + @node Attribute Syntax @section Attribute Syntax @cindex attribute syntax @@ -6940,6 +6984,8 @@ and enumerated types. applying to labels. @xref{Enumerator Attributes}, for details of the semantics of attributes applying to enumerators. +@xref{Statement Attributes}, for details of the semantics of attributes +applying to statements. An @dfn{attribute specifier} is of the form @code{__attribute__ ((@var{attribute-list}))}. An @dfn{attribute list} @@ -7005,6 +7051,10 @@ present. The optional attribute in the enumerator appertains to the enumeration constant. It is not possible to place the attribute after the constant expression, if present. +@subsubheading Statement Attributes +In GNU C, an attribute specifier list may appear as part of a null +statement. The attribute goes before the semicolon. + @subsubheading Type Attributes An attribute specifier list may appear as part of a @code{struct}, diff --git gcc/gcc/doc/invoke.texi gcc/gcc/doc/invoke.texi index 22001f9..29052f3 100644 --- gcc/gcc/doc/invoke.texi +++ gcc/gcc/doc/invoke.texi @@ -272,8 +272,8 @@ Objective-C and Objective-C++ Dialects}. -Wformat-security -Wformat-signedness -Wformat-y2k -Wframe-address @gol -Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol -Wignored-qualifiers -Wignored-attributes -Wincompatible-pointer-types @gol --Wimplicit -Wimplicit-function-declaration -Wimplicit-int @gol --Winit-self -Winline -Wno-int-conversion @gol +-Wimplicit -Wimplicit-fallthrough -Wimplicit-function-declaration @gol +-Wimplicit-int -Winit-self -Winline -Wno-int-conversion @gol -Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol -Winvalid-pch -Wlarger-than=@var{len} @gol -Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol @@ -3653,6 +3653,7 @@ name is still supported, but the newer name is more descriptive.) @gccoptlist{-Wclobbered @gol -Wempty-body @gol -Wignored-qualifiers @gol +-Wimplicit-fallthrough @gol -Wmissing-field-initializers @gol -Wmissing-parameter-type @r{(C only)} @gol -Wold-style-declaration @r{(C only)} @gol @@ -3939,6 +3940,91 @@ enabled by default and it is made into an error by Same as @option{-Wimplicit-int} and @option{-Wimplicit-function-declaration}. This warning is enabled by @option{-Wall}. +@item -Wimplicit-fallthrough +@opindex Wimplicit-fallthrough +@opindex Wno-implicit-fallthrough +Warn when a switch case falls through. For example: + +@smallexample +@group +switch (cond) + @{ + case 1: + a = 1; + break; + case 2: + a = 2; + case 3: + a = 3; + break; + @} +@end group +@end smallexample + +This warning does not warn when the last statement of a case cannot +fall through, e.g. when there is a return statement of a function +declared with the noreturn attribute. @option{-Wimplicit-fallthrough} +also takes into account control flow statements, such as ifs, and only +warns when appropriate. E.g.@: + +@smallexample +@group +switch (cond) + @{ + case 1: + if (i > 3) @{ + bar (5); + break; + @} else if (i < 1) @{ + bar (0); + @} else + return; + default: + @dots{} + @} +@end group +@end smallexample + +Since there are occasions where a switch case fall through is desirable, +GCC provides an attribute, @code{__attribute__ ((fallthrough))}, that is +to be used along with a null statement to suppress this warning that +would normally occur: + +@smallexample +@group +switch (cond) + @{ + case 1: + bar (0); + __attribute__ ((fallthrough)); + default: + @dots{} + @} +@end group +@end smallexample + +C++17 provides a standard way to suppress the @option{-Wimplicit-fallthrough} +warning using @code{[[fallthrough]];} instead of the GNU attribute. In C++11 +or C++14 users can use @code{[[gnu::fallthrough]];}, which is a GNU extension. +Instead of the these attributes, it is also possible to add a "falls through" +comment to silence the warning. GCC accepts wide range of such comments, so +e.g. all of "Falls through.", "fallthru", "FALLS-THROUGH" work. + +@smallexample +@group +switch (cond) + @{ + case 1: + bar (0); + /* FALLTHRU */ + default: + @dots{} + @} +@end group +@end smallexample + +This warning is enabled by @option{-Wextra}. + @item -Wignored-qualifiers @r{(C and C++ only)} @opindex Wignored-qualifiers @opindex Wno-ignored-qualifiers diff --git gcc/gcc/gimple.h gcc/gcc/gimple.h index 980bdf8..9fad15b 100644 --- gcc/gcc/gimple.h +++ gcc/gcc/gimple.h @@ -2921,6 +2921,16 @@ gimple_call_internal_unique_p (const gimple *gs) return gimple_call_internal_unique_p (gc); } +/* Return true if GS is an internal function FN. */ + +static inline bool +gimple_call_internal_p (const gimple *gs, internal_fn fn) +{ + return (is_gimple_call (gs) + && gimple_call_internal_p (gs) + && gimple_call_internal_fn (gs) == fn); +} + /* If CTRL_ALTERING_P is true, mark GIMPLE_CALL S to be a stmt that could alter control flow. */ diff --git gcc/gcc/gimplify.c gcc/gcc/gimplify.c index fb27dd0..7529fb2 100644 --- gcc/gcc/gimplify.c +++ gcc/gcc/gimplify.c @@ -160,6 +160,7 @@ struct gimplify_ctx unsigned in_cleanup_point_expr : 1; unsigned keep_stack : 1; unsigned save_stack : 1; + unsigned in_switch_expr : 1; }; struct gimplify_omp_ctx @@ -1626,6 +1627,338 @@ maybe_warn_switch_unreachable (gimple_seq seq) } } + +/* A label entry that pairs label and a location. */ +struct label_entry +{ + tree label; + location_t loc; +}; + +/* Find LABEL in vector of label entries VEC. */ + +static struct label_entry * +find_label_entry (const auto_vec <struct label_entry> *vec, tree label) +{ + unsigned int i; + struct label_entry *l; + + FOR_EACH_VEC_ELT (*vec, i, l) + if (l->label == label) + return l; + return NULL; +} + +/* Find the last statement in a scope STMT. */ + +static gimple * +last_stmt_in_scope (gimple *stmt) +{ + if (!stmt) + return NULL; + + switch (gimple_code (stmt)) + { + case GIMPLE_BIND: + { + gbind *bind = as_a <gbind *> (stmt); + return last_stmt_in_scope ( + gimple_seq_last_stmt (gimple_bind_body (bind))); + } + + case GIMPLE_TRY: + { + gtry *try_stmt = as_a <gtry *> (stmt); + return last_stmt_in_scope ( + gimple_seq_last_stmt (gimple_try_eval (try_stmt))); + } + + default: + return stmt; + } +} + +/* Callback for walk_gimple_seq. */ + +static tree +warn_implicit_fallthrough_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, + struct walk_stmt_info *) +{ + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + switch (gimple_code (stmt)) + { + case GIMPLE_TRY: + case GIMPLE_BIND: + case GIMPLE_CATCH: + case GIMPLE_EH_FILTER: + case GIMPLE_TRANSACTION: + /* Walk the sub-statements. */ + *handled_ops_p = false; + break; + + /* Find a sequence of form: + + GIMPLE_LABEL + [...] + <may fallthru stmt> + GIMPLE_LABEL + + and possibly warn. */ + case GIMPLE_LABEL: + { + /* Found a label. Skip all immediately following labels. */ + while (!gsi_end_p (*gsi_p) + && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL) + gsi_next (gsi_p); + + /* There might be no more statements. */ + if (gsi_end_p (*gsi_p)) + return integer_zero_node; + + /* Next statements, if any, are non-label. */ + gimple *prev = NULL; + /* Vector of labels that fall through. */ + auto_vec <struct label_entry> labels; + + do + { + if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_BIND + || gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_TRY) + { + /* Nested scope. Only look at the last statement of + the innermost scope. */ + location_t bind_loc = gimple_location (gsi_stmt (*gsi_p)); + gimple *last = last_stmt_in_scope (gsi_stmt (*gsi_p)); + if (last) + { + prev = last; + /* It might be a label without a location. Use the + location of the scope then. */ + if (!gimple_has_location (prev)) + gimple_set_location (prev, bind_loc); + } + gsi_next (gsi_p); + continue; + } + /* If's are tricky. */ + if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_COND) + { + gcond *cond_stmt = as_a <gcond *> (gsi_stmt (*gsi_p)); + tree false_lab = gimple_cond_false_label (cond_stmt); + location_t if_loc = gimple_location (cond_stmt); + + /* If we have e.g. + if (i > 1) goto <D.2259>; else goto D; + we can't do much with the else-branch. */ + if (!DECL_ARTIFICIAL (false_lab)) + break; + + /* Go on until the false label, then one step back. */ + for (; !gsi_end_p (*gsi_p); gsi_next (gsi_p)) + { + stmt = gsi_stmt (*gsi_p); + if (gimple_code (stmt) == GIMPLE_LABEL + && gimple_label_label (as_a <glabel *> (stmt)) + == false_lab) + break; + } + + /* Not found? Oops. */ + if (gsi_end_p (*gsi_p)) + break; + + { + struct label_entry l = { false_lab, if_loc }; + labels.safe_push (l); + } + + /* Go to the last statement of the then branch. */ + gsi_prev (gsi_p); + + /* if (i != 0) goto <D.1759>; else goto <D.1760>; + <D.1759>: + <stmt>; + goto <D.1761>; + <D.1760>: + */ + if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO + && !gimple_has_location (gsi_stmt (*gsi_p))) + { + /* Look at the statement before, it might be + __attribute__((fallthrough)), in which case don't + warn. */ + gsi_prev (gsi_p); + bool fallthru_before_dest + = gimple_call_internal_p (gsi_stmt (*gsi_p), + IFN_FALLTHROUGH); + gsi_next (gsi_p); + tree goto_dest = gimple_goto_dest (gsi_stmt (*gsi_p)); + if (!fallthru_before_dest) + { + struct label_entry l = { goto_dest, if_loc }; + labels.safe_push (l); + } + } + /* And move back. */ + gsi_next (gsi_p); + } + /* Remember the last statement. Skip labels that are of no + interest to us. */ + if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL) + { + tree label + = gimple_label_label (as_a <glabel *> (gsi_stmt (*gsi_p))); + if (find_label_entry (&labels, label)) + prev = gsi_stmt (*gsi_p); + } + else + prev = gsi_stmt (*gsi_p); + gsi_next (gsi_p); + } + while (!gsi_end_p (*gsi_p) + /* Stop if we find a case or a user-defined label. */ + && (gimple_code (gsi_stmt (*gsi_p)) != GIMPLE_LABEL + || !gimple_has_location (gsi_stmt (*gsi_p)))); + + /* There might be no more statements. */ + if (gsi_end_p (*gsi_p)) + return integer_zero_node; + + gimple *next = gsi_stmt (*gsi_p); + tree label; + /* If what follows is a label, then we may have a fallthrough. */ + if (gimple_code (next) == GIMPLE_LABEL + && gimple_has_location (next) + && (label = gimple_label_label (as_a <glabel *> (next))) + && !FALLTHROUGH_LABEL_P (label) + && prev != NULL) + { + /* Don't warn for terminated branches, i.e. when the subsequent + case labels immediately breaks. */ + gimple_stmt_iterator gsi2 = *gsi_p; + bool fallthru_to_break_p = false; + + /* Skip all immediately following labels. */ + while (!gsi_end_p (gsi2) + && gimple_code (gsi_stmt (gsi2)) == GIMPLE_LABEL) + gsi_next (&gsi2); + /* { ... something; default:; } */ + if (gsi_end_p (gsi2) + /* { ... something; default: break; } or + { ... something; default: goto L; } */ + || gimple_code (gsi_stmt (gsi2)) == GIMPLE_GOTO + /* { ... something; default: return; } */ + || gimple_code (gsi_stmt (gsi2)) == GIMPLE_RETURN) + fallthru_to_break_p = true; + + struct label_entry *l; + bool warned_p = false; + if (fallthru_to_break_p) + /* Quiet. */; + else if (gimple_code (prev) == GIMPLE_LABEL + && (label = gimple_label_label (as_a <glabel *> (prev))) + && (l = find_label_entry (&labels, label))) + warned_p = warning_at (l->loc, OPT_Wimplicit_fallthrough, + "this statement may fall through"); + else if (!gimple_call_internal_p (prev, IFN_FALLTHROUGH) + /* Try to be clever and don't warn when the statement + can't actually fall through. */ + && gimple_stmt_may_fallthru (prev) + && gimple_has_location (prev)) + warned_p = warning_at (gimple_location (prev), + OPT_Wimplicit_fallthrough, + "this statement may fall through"); + if (warned_p) + { + rich_location richloc (line_table, gimple_location (next)); + richloc.add_fixit_insert (gimple_location (next), + "insert '__attribute__ " + "((fallthrough));' to silence " + "this warning"); + richloc.add_fixit_insert (gimple_location (next), + "insert 'break;' to avoid " + "fall-through"); + inform_at_rich_loc (&richloc, "here"); + } + + /* So that next warn_implicit_fallthrough_r will start looking for + a new sequence starting with this label. */ + gsi_prev (gsi_p); + } + } + break; + default: + break; + } + return NULL_TREE; +} + +/* Warn when a switch case falls through. */ + +static void +maybe_warn_implicit_fallthrough (gimple_seq seq) +{ + if (!warn_implicit_fallthrough || lang_GNU_Fortran ()) + return; + + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + walk_gimple_seq (seq, warn_implicit_fallthrough_r, NULL, &wi); +} + +/* Callback for walk_gimple_seq. */ + +static tree +expand_FALLTHROUGH_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, + struct walk_stmt_info *) +{ + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + switch (gimple_code (stmt)) + { + case GIMPLE_TRY: + case GIMPLE_BIND: + case GIMPLE_CATCH: + case GIMPLE_EH_FILTER: + case GIMPLE_TRANSACTION: + /* Walk the sub-statements. */ + *handled_ops_p = false; + break; + case GIMPLE_CALL: + if (gimple_call_internal_p (stmt, IFN_FALLTHROUGH)) + { + gsi_remove (gsi_p, true); + if (gsi_end_p (*gsi_p)) + return integer_zero_node; + else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_LABEL + || (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO + && !gimple_has_location (gsi_stmt (*gsi_p)))) + /* This usage is OK. */; + else + warning_at (gimple_location (stmt), 0, + "%<__attribute__ ((fallthrough))%> not preceding " + "a label"); + } + break; + default: + break; + } + return NULL_TREE; +} + +/* Expand all FALLTHROUGH () calls in SEQ. */ + +static void +expand_FALLTHROUGH (gimple_seq *seq_p) +{ + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + walk_gimple_seq_mod (seq_p, expand_FALLTHROUGH_r, NULL, &wi); +} + /* Gimplify a SWITCH_EXPR, and collect the vector of labels it can branch to. */ @@ -1660,10 +1993,17 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p) labels. Save all the things from the switch body to append after. */ saved_labels = gimplify_ctxp->case_labels; gimplify_ctxp->case_labels.create (8); + bool old_in_switch_expr = gimplify_ctxp->in_switch_expr; + gimplify_ctxp->in_switch_expr = true; gimplify_stmt (&SWITCH_BODY (switch_expr), &switch_body_seq); + gimplify_ctxp->in_switch_expr = old_in_switch_expr; maybe_warn_switch_unreachable (switch_body_seq); + maybe_warn_implicit_fallthrough (switch_body_seq); + /* Only do this for the innermost GIMPLE_SWITCH. */ + if (!gimplify_ctxp->in_switch_expr) + expand_FALLTHROUGH (&switch_body_seq); labels = gimplify_ctxp->case_labels; gimplify_ctxp->case_labels = saved_labels; @@ -1694,6 +2034,21 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p) return GS_ALL_DONE; } +/* Gimplify the LABEL_EXPR pointed to by EXPR_P. */ + +static enum gimplify_status +gimplify_label_expr (tree *expr_p, gimple_seq *pre_p) +{ + gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p)) + == current_function_decl); + + glabel *label_stmt = gimple_build_label (LABEL_EXPR_LABEL (*expr_p)); + gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p)); + gimplify_seq_add_stmt (pre_p, label_stmt); + + return GS_ALL_DONE; +} + /* Gimplify the CASE_LABEL_EXPR pointed to by EXPR_P. */ static enum gimplify_status @@ -1711,6 +2066,7 @@ gimplify_case_label_expr (tree *expr_p, gimple_seq *pre_p) break; label_stmt = gimple_build_label (CASE_LABEL (*expr_p)); + gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p)); ctxp->case_labels.safe_push (*expr_p); gimplify_seq_add_stmt (pre_p, label_stmt); @@ -10704,11 +11060,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, break; case LABEL_EXPR: - ret = GS_ALL_DONE; - gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p)) - == current_function_decl); - gimplify_seq_add_stmt (pre_p, - gimple_build_label (LABEL_EXPR_LABEL (*expr_p))); + ret = gimplify_label_expr (expr_p, pre_p); break; case CASE_LABEL_EXPR: diff --git gcc/gcc/internal-fn.c gcc/gcc/internal-fn.c index 49f3495..358757f 100644 --- gcc/gcc/internal-fn.c +++ gcc/gcc/internal-fn.c @@ -243,6 +243,15 @@ expand_TSAN_FUNC_EXIT (internal_fn, gcall *) gcc_unreachable (); } +/* This should get expanded in the lower pass. */ + +static void +expand_FALLTHROUGH (internal_fn, gcall *call) +{ + error_at (gimple_location (call), + "invalid use of %<__attribute__((fallthrough));%>"); +} + /* Helper function for expand_addsub_overflow. Return 1 if ARG interpreted as signed in its precision is known to be always positive or 2 if ARG is known to be always negative, or 3 if ARG may diff --git gcc/gcc/internal-fn.def gcc/gcc/internal-fn.def index 6701cd9..d4fbdb2 100644 --- gcc/gcc/internal-fn.def +++ gcc/gcc/internal-fn.def @@ -195,6 +195,9 @@ DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_COMPLEMENT, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ATOMIC_BIT_TEST_AND_RESET, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ATOMIC_COMPARE_EXCHANGE, ECF_LEAF | ECF_NOTHROW, NULL) +/* To implement [[fallthrough]]. */ +DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF | ECF_NOTHROW, NULL) + #undef DEF_INTERNAL_INT_FN #undef DEF_INTERNAL_FLT_FN #undef DEF_INTERNAL_OPTAB_FN diff --git gcc/gcc/system.h gcc/gcc/system.h index 78a7da6..1eba393 100644 --- gcc/gcc/system.h +++ gcc/gcc/system.h @@ -746,6 +746,12 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN; #define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__)) #endif +#if GCC_VERSION >= 7000 +# define gcc_fallthrough() __attribute__((fallthrough)) +#else +# define gcc_fallthrough() +#endif + #if GCC_VERSION >= 3001 #define STATIC_CONSTANT_P(X) (__builtin_constant_p (X) && (X)) #else diff --git gcc/gcc/tree-core.h gcc/gcc/tree-core.h index 6e8595c..fb1cc8f 100644 --- gcc/gcc/tree-core.h +++ gcc/gcc/tree-core.h @@ -1043,6 +1043,9 @@ struct GTY(()) tree_base { TRANSACTION_EXPR_RELAXED in TRANSACTION_EXPR + FALLTHROUGH_LABEL_P in + LABEL_DECL + private_flag: TREE_PRIVATE in diff --git gcc/gcc/tree.h gcc/gcc/tree.h index e2ffabf..52c584f 100644 --- gcc/gcc/tree.h +++ gcc/gcc/tree.h @@ -771,6 +771,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, computed gotos. */ #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag) +/* Whether a case or a user-defined label is allowed to fall through to. + This is used to implement -Wimplicit-fallthrough. */ +#define FALLTHROUGH_LABEL_P(NODE) \ + (LABEL_DECL_CHECK (NODE)->base.public_flag) + /* Nonzero means this expression is volatile in the C sense: its address should be of type `volatile WHATEVER *'. In other words, the declared item is volatile qualified. diff --git gcc/libcpp/include/cpplib.h gcc/libcpp/include/cpplib.h index 543f3b9..550318a 100644 --- gcc/libcpp/include/cpplib.h +++ gcc/libcpp/include/cpplib.h @@ -185,7 +185,8 @@ struct GTY(()) cpp_string { #define STRINGIFY_ARG (1 << 2) /* If macro argument to be stringified. */ #define PASTE_LEFT (1 << 3) /* If on LHS of a ## operator. */ #define NAMED_OP (1 << 4) /* C++ named operators. */ -#define NO_EXPAND (1 << 5) /* Do not macro-expand this token. */ +#define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH + comment. */ #define BOL (1 << 6) /* Token at beginning of line. */ #define PURE_ZERO (1 << 7) /* Single 0 digit, used by the C++ frontend, set in c-lex.c. */ @@ -193,6 +194,7 @@ struct GTY(()) cpp_string { #define SP_PREV_WHITE (1 << 9) /* If whitespace before a ## operator, or before this token after a # operator. */ +#define NO_EXPAND (1 << 10) /* Do not macro-expand this token. */ /* Specify which field, if any, of the cpp_token union is used. */ diff --git gcc/libcpp/lex.c gcc/libcpp/lex.c index 236418d..a806e6c 100644 --- gcc/libcpp/lex.c +++ gcc/libcpp/lex.c @@ -2031,6 +2031,94 @@ save_comment (cpp_reader *pfile, cpp_token *token, const unsigned char *from, store_comment (pfile, token); } +/* Returns true if comment at COMMENT_START is a recognized FALLTHROUGH + comment. */ + +static bool +fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start) +{ + const unsigned char *from = comment_start + 1; + /* Whole comment contents: + -fallthrough + @fallthrough@ + */ + if (*from == '-' || *from == '@') + { + size_t l = sizeof "fallthrough" - 1; + if ((size_t) (pfile->buffer->cur - from - 1) < l) + return false; + if (memcmp (from + 1, "fallthrough", l)) + return false; + if (*from == '@') + { + if (from[l + 1] != '@') + return false; + l++; + } + from += 1 + l; + } + /* Whole comment contents (regex): + [ \t]*FALL(S | |-)?THR(OUGH|U)\.?[ \t]* + [ \t]*Fall(s | |-)?[Tt]hr(ough|u)\.?[ \t]* + [ \t]*fall(s | |-)?thr(ough|u)\.?[ \t]* + */ + else + { + while (*from == ' ' || *from == '\t') + from++; + unsigned char f = *from; + if (f != 'F' && f != 'f') + return false; + if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthrough") + return false; + bool all_upper = false; + if (f == 'F' && memcmp (from + 1, "ALL", sizeof "ALL" - 1) == 0) + all_upper = true; + else if (memcmp (from + 1, "all", sizeof "all" - 1)) + return false; + if (from[sizeof "fall" - 1] == (all_upper ? 'S' : 's') + && from[sizeof "falls" - 1] == ' ') + from += sizeof "falls " - 1; + else if (from[sizeof "fall" - 1] == ' ' + || from[sizeof "fall" - 1] == '-') + from += sizeof "fall " - 1; + else if (from[sizeof "fall" - 1] != (all_upper ? 'T' : 't')) + return false; + else + from += sizeof "fall" - 1; + if ((f == 'f' || *from != 'T') && (all_upper || *from != 't')) + return false; + if ((size_t) (pfile->buffer->cur - from) < sizeof "thru") + return false; + if (memcmp (from + 1, all_upper ? "HRU" : "hru", sizeof "hru" - 1)) + { + if ((size_t) (pfile->buffer->cur - from) < sizeof "through") + return false; + if (memcmp (from + 1, all_upper ? "HROUGH" : "hrough", + sizeof "hrough" - 1)) + return false; + from += sizeof "through" - 1; + } + else + from += sizeof "thru" - 1; + if (*from == '.') + from++; + while (*from == ' ' || *from == '\t') + from++; + } + /* C block comment. */ + if (*comment_start == '*') + { + if (*from != '*' || from[1] != '/') + return false; + } + /* C++ line comment. */ + else if (*from != '\n') + return false; + + return true; +} + /* Allocate COUNT tokens for RUN. */ void _cpp_init_tokenrun (tokenrun *run, unsigned int count) @@ -2309,7 +2397,7 @@ _cpp_lex_direct (cpp_reader *pfile) { cppchar_t c; cpp_buffer *buffer; - const unsigned char *comment_start; + const unsigned char *comment_start = NULL; cpp_token *result = pfile->cur_token++; fresh_line: @@ -2336,6 +2424,8 @@ _cpp_lex_direct (cpp_reader *pfile) } return result; } + if (buffer != pfile->buffer) + comment_start = NULL; if (!pfile->keep_tokens) { pfile->cur_run = &pfile->base_run; @@ -2442,6 +2532,11 @@ _cpp_lex_direct (cpp_reader *pfile) result->flags |= NAMED_OP; result->type = (enum cpp_ttype) result->val.node.node->directive_index; } + + /* Signal FALLTHROUGH comment followed by another token. */ + if (comment_start + && fallthrough_comment_p (pfile, comment_start)) + result->flags |= PREV_FALLTHROUGH; break; case '\'': @@ -2533,6 +2628,9 @@ _cpp_lex_direct (cpp_reader *pfile) goto update_tokens_line; } + if (fallthrough_comment_p (pfile, comment_start)) + result->flags |= PREV_FALLTHROUGH; + /* Save the comment as a token in its own right. */ save_comment (pfile, result, comment_start, c); break; Marek