diff options
-rw-r--r-- | gcc/c/ChangeLog | 45 | ||||
-rw-r--r-- | gcc/c/c-decl.c | 110 | ||||
-rw-r--r-- | gcc/c/c-parser.c | 427 | ||||
-rw-r--r-- | gcc/c/c-parser.h | 3 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 25 | ||||
-rw-r--r-- | gcc/c/gimple-parser.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c | 21 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c | 55 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c | 60 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c | 56 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c | 16 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c | 16 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu2x-attrs-1.c | 72 |
14 files changed, 792 insertions, 123 deletions
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 95b0eb6..228ebd8 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,48 @@ +2019-11-14 Joseph Myers <joseph@codesourcery.com> + + * c-tree.h (enum c_typespec_kind): Add ctsk_tagref_attrs and + ctsk_tagfirstref_attrs. + (struct c_declspecs): Update description of attrs. Add + postfix_attrs and non_std_attrs_seen_p. Increase size of + typespec_kind bit-field. + (c_warn_unused_attributes): New declaration. + (parser_xref_tag): Update prototype. + * c-decl.c (c_warn_unused_attributes): New function. + (shadow_tag_warned): Handle ctsk_tagfirstref_attrs and + ctsk_tagref_attrs. Handle attribute declarations. + (check_compound_literal_type): Handle ctsk_tagfirstref_attrs. + (grokdeclarator): Handle standard attributes. + (parser_xref_tag): Add arguments have_std_attrs and attrs. Apply + attributes to incomplete type reference. + (xref_tag): Update call to parser_xref_tag. + (declspecs_add_addrspace, declspecs_add_type) + (declspecs_add_scspec, declspecs_add_attrs): Set + non_std_attrs_seen_p. + (finish_declspecs): Apply postfix standard attributes to type. + * c-parser.c (c_token_starts_declspecs) + (c_token_starts_declaration, c_parser_next_token_starts_declspecs) + (c_parser_next_tokens_start_declaration): Update comments. + (c_parser_consume_token, c_parser_consume_pragma): Handle moving + parser->tokens[2] to parser->tokens[1]. + (c_parser_nth_token_starts_std_attributes) + (c_parser_std_attribute_specifier_sequence): New functions. + (c_parser_declaration_or_fndef): Add arguments have_attrs and + attrs. All callers changed. Handle standard attributes. + (c_parser_parms_declarator, c_parser_parms_list_declarator) + (c_parser_parameter_declaration): Add argument have_gnu_attrs. + All callers changed. + (c_parser_declspecs): Add arguments start_std_attr_ok and + end_std_attr_ok. All callers changed. Handle standard + attributes. + (c_parser_enum_specifier, c_parser_struct_or_union_specifier) + (c_parser_direct_declarator, c_parser_direct_declarator_inner) + (c_parser_compound_statement_nostart, c_parser_all_labels) + (c_parser_label, c_parser_statement, c_parser_for_statement): + Handle standard attributes. + * c-parser.h (c_parser_declspecs): Update prototype. + * gimple-parser.c (c_parser_gimple_declaration): Update call to + c_parser_declspecs. + 2019-11-12 Martin Liska <mliska@suse.cz> * gimple-parser.c: Do not include params.h. diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 2841b4f..f809059 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -4491,6 +4491,18 @@ c_simulate_builtin_function_decl (tree decl) C_DECL_BUILTIN_PROTOTYPE (decl) = prototype_p (type); return pushdecl (decl); } + +/* Warn about attributes in a context where they are unused + (attribute-declarations, except for the "fallthrough" case, and + attributes on statements). */ + +void +c_warn_unused_attributes (tree attrs) +{ + for (tree t = attrs; t != NULL_TREE; t = TREE_CHAIN (t)) + warning (OPT_Wattributes, "%qE attribute ignored", + get_attribute_name (t)); +} /* Called when a declaration is seen that contains no names to declare. If its type is a reference to a structure, union or enum inherited @@ -4545,6 +4557,7 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) } else if (declspecs->typespec_kind != ctsk_tagdef && declspecs->typespec_kind != ctsk_tagfirstref + && declspecs->typespec_kind != ctsk_tagfirstref_attrs && declspecs->storage_class != csc_none) { if (warned != 1) @@ -4556,6 +4569,7 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) } else if (declspecs->typespec_kind != ctsk_tagdef && declspecs->typespec_kind != ctsk_tagfirstref + && declspecs->typespec_kind != ctsk_tagfirstref_attrs && (declspecs->const_p || declspecs->volatile_p || declspecs->atomic_p @@ -4571,6 +4585,7 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) } else if (declspecs->typespec_kind != ctsk_tagdef && declspecs->typespec_kind != ctsk_tagfirstref + && declspecs->typespec_kind != ctsk_tagfirstref_attrs && declspecs->alignas_p) { if (warned != 1) @@ -4668,9 +4683,34 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) warned = 2; } + if (found_tag + && warned == 2 + && (declspecs->typespec_kind == ctsk_tagref_attrs + || declspecs->typespec_kind == ctsk_tagfirstref_attrs)) + { + /* Standard attributes after the "struct" or "union" keyword are + only permitted when the contents of the type are defined, or + in the form "struct-or-union attribute-specifier-sequence + identifier;". If the ';' was not present, attributes were + diagnosed in the parser. Here, ensure that any other useless + elements of the declaration result in a pedwarn, not just a + warning. Forward declarations of enum types are not part of + standard C, but handle them the same. */ + pedwarn (input_location, 0, + "invalid use of attributes in empty declaration"); + warned = 1; + } + if (warned != 1) { - if (!found_tag) + if (declspecs->declspecs_seen_p + && !declspecs->non_std_attrs_seen_p) + /* An attribute declaration (but not a fallthrough attribute + declaration, which was handled separately); warn if there + are any attributes being ignored (but not if the attributes + were empty). */ + c_warn_unused_attributes (declspecs->attrs); + else if (!found_tag) pedwarn (input_location, 0, "empty declaration"); } } @@ -5605,7 +5645,8 @@ check_compound_literal_type (location_t loc, struct c_type_name *type_name) { if (warn_cxx_compat && (type_name->specs->typespec_kind == ctsk_tagdef - || type_name->specs->typespec_kind == ctsk_tagfirstref)) + || type_name->specs->typespec_kind == ctsk_tagfirstref + || type_name->specs->typespec_kind == ctsk_tagfirstref_attrs)) warning_at (loc, OPT_Wc___compat, "defining a type in a compound literal is invalid in C++"); } @@ -6210,18 +6251,32 @@ grokdeclarator (const struct c_declarator *declarator, const struct c_declarator *inner_decl; int attr_flags = 0; declarator = declarator->declarator; + /* Standard attribute syntax precisely defines what entity + an attribute in each position appertains to, so only + apply laxity about positioning to GNU attribute syntax. + Standard attributes applied to a function or array + declarator apply exactly to that type; standard + attributes applied to the identifier apply to the + declaration rather than to the type. */ inner_decl = declarator; while (inner_decl->kind == cdk_attrs) inner_decl = inner_decl->declarator; - if (inner_decl->kind == cdk_id) - attr_flags |= (int) ATTR_FLAG_DECL_NEXT; - else if (inner_decl->kind == cdk_function) - attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT; - else if (inner_decl->kind == cdk_array) - attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT; - returned_attrs = decl_attributes (&type, - chainon (returned_attrs, attrs), - attr_flags); + if (!cxx11_attribute_p (attrs)) + { + if (inner_decl->kind == cdk_id) + attr_flags |= (int) ATTR_FLAG_DECL_NEXT; + else if (inner_decl->kind == cdk_function) + attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT; + else if (inner_decl->kind == cdk_array) + attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT; + } + if (cxx11_attribute_p (attrs) && inner_decl->kind == cdk_id) + returned_attrs = chainon (returned_attrs, attrs); + else + returned_attrs = decl_attributes (&type, + chainon (returned_attrs, + attrs), + attr_flags); break; } case cdk_array: @@ -7686,11 +7741,14 @@ get_parm_info (bool ellipsis, tree expr) /* Get the struct, enum or union (CODE says which) with tag NAME. Define the tag as a forward-reference with location LOC if it is - not defined. Return a c_typespec structure for the type - specifier. */ + not defined. HAVE_STD_ATTRS says whether any standard attributes + were present after the struct, union or enum keyword; ATTRS are the + standard attributes present there. Return a c_typespec structure + for the type specifier. */ struct c_typespec -parser_xref_tag (location_t loc, enum tree_code code, tree name) +parser_xref_tag (location_t loc, enum tree_code code, tree name, + bool have_std_attrs, tree attrs) { struct c_typespec ret; tree ref; @@ -7714,9 +7772,12 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name) this would not work properly if we return the reference found. (For example, with "struct foo" in an outer scope, "union foo;" must shadow that tag with a new one of union type.) */ - ret.kind = (ref ? ctsk_tagref : ctsk_tagfirstref); + ret.kind = (ref + ? (have_std_attrs ? ctsk_tagref_attrs : ctsk_tagref) + : (have_std_attrs ? ctsk_tagfirstref_attrs : ctsk_tagfirstref)); if (ref && TREE_CODE (ref) == code) { + decl_attributes (&ref, attrs, (int) ATTR_FLAG_TYPE_IN_PLACE); if (C_TYPE_DEFINED_IN_STRUCT (ref) && loc != UNKNOWN_LOCATION && warn_cxx_compat) @@ -7770,6 +7831,7 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name) } pushtag (loc, name, ref); + decl_attributes (&ref, attrs, (int) ATTR_FLAG_TYPE_IN_PLACE); ret.spec = ref; return ret; @@ -7782,7 +7844,7 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name) tree xref_tag (enum tree_code code, tree name) { - return parser_xref_tag (input_location, code, name).spec; + return parser_xref_tag (input_location, code, name, false, NULL_TREE).spec; } /* Make sure that the tag NAME is defined *in the current scope* @@ -10214,6 +10276,7 @@ declspecs_add_addrspace (location_t location, { specs->non_sc_seen_p = true; specs->declspecs_seen_p = true; + specs->non_std_attrs_seen_p = true; if (!ADDR_SPACE_GENERIC_P (specs->address_space) && specs->address_space != as) @@ -10239,6 +10302,7 @@ declspecs_add_qual (location_t loc, bool dupe = false; specs->non_sc_seen_p = true; specs->declspecs_seen_p = true; + specs->non_std_attrs_seen_p = true; gcc_assert (TREE_CODE (qual) == IDENTIFIER_NODE && C_IS_RESERVED_WORD (qual)); i = C_RID_CODE (qual); @@ -10297,6 +10361,7 @@ declspecs_add_type (location_t loc, struct c_declspecs *specs, tree type = spec.spec; specs->non_sc_seen_p = true; specs->declspecs_seen_p = true; + specs->non_std_attrs_seen_p = true; specs->typespec_kind = spec.kind; if (TREE_DEPRECATED (type)) specs->deprecated_p = true; @@ -11162,6 +11227,7 @@ declspecs_add_scspec (location_t loc, enum c_storage_class n = csc_none; bool dupe = false; specs->declspecs_seen_p = true; + specs->non_std_attrs_seen_p = true; gcc_assert (TREE_CODE (scspec) == IDENTIFIER_NODE && C_IS_RESERVED_WORD (scspec)); i = C_RID_CODE (scspec); @@ -11278,6 +11344,9 @@ declspecs_add_attrs (location_t loc, struct c_declspecs *specs, tree attrs) specs->attrs = chainon (attrs, specs->attrs); specs->locations[cdw_attributes] = loc; specs->declspecs_seen_p = true; + /* In the case of standard attributes at the start of the + declaration, the caller will reset this. */ + specs->non_std_attrs_seen_p = true; return specs; } @@ -11306,7 +11375,7 @@ declspecs_add_alignas (location_t loc, specifiers with any other type specifier to determine the resulting type. This is where ISO C checks on complex types are made, since "_Complex long" is a prefix of the valid ISO C type "_Complex long - double". */ + double". Also apply postfix standard attributes to modify the type. */ struct c_declspecs * finish_declspecs (struct c_declspecs *specs) @@ -11376,6 +11445,8 @@ finish_declspecs (struct c_declspecs *specs) && !specs->signed_p && !specs->unsigned_p && !specs->complex_p); /* Type to be filled in later. */ + if (specs->postfix_attrs) + error ("%<__auto_type%> followed by %<[[]]%> attributes"); break; case cts_void: gcc_assert (!specs->long_p && !specs->short_p @@ -11581,6 +11652,11 @@ finish_declspecs (struct c_declspecs *specs) default: gcc_unreachable (); } + if (specs->type != NULL) + { + decl_attributes (&specs->type, specs->postfix_attrs, 0); + specs->postfix_attrs = NULL_TREE; + } return specs; } diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 8d7ecf4..5f8695c 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -633,8 +633,8 @@ c_parser_next_token_is_qualifier (c_parser *parser) return c_token_is_qualifier (token); } -/* Return true if TOKEN can start declaration specifiers, false - otherwise. */ +/* Return true if TOKEN can start declaration specifiers (not + including standard attributes), false otherwise. */ static bool c_token_starts_declspecs (c_token *token) { @@ -713,8 +713,9 @@ c_token_starts_declspecs (c_token *token) } -/* Return true if TOKEN can start declaration specifiers or a static - assertion, false otherwise. */ +/* Return true if TOKEN can start declaration specifiers (not + including standard attributes) or a static assertion, false + otherwise. */ static bool c_token_starts_declaration (c_token *token) { @@ -726,7 +727,8 @@ c_token_starts_declaration (c_token *token) } /* Return true if the next token from PARSER can start declaration - specifiers, false otherwise. */ + specifiers (not including standard attributes), false + otherwise. */ bool c_parser_next_token_starts_declspecs (c_parser *parser) { @@ -748,7 +750,8 @@ c_parser_next_token_starts_declspecs (c_parser *parser) } /* Return true if the next tokens from PARSER can start declaration - specifiers or a static assertion, false otherwise. */ + specifiers (not including standard attributes) or a static + assertion, false otherwise. */ bool c_parser_next_tokens_start_declaration (c_parser *parser) { @@ -787,8 +790,12 @@ c_parser_consume_token (c_parser *parser) parser->last_token_location = parser->tokens[0].location; if (parser->tokens != &parser->tokens_buf[0]) parser->tokens++; - else if (parser->tokens_avail == 2) - parser->tokens[0] = parser->tokens[1]; + else if (parser->tokens_avail >= 2) + { + parser->tokens[0] = parser->tokens[1]; + if (parser->tokens_avail >= 3) + parser->tokens[1] = parser->tokens[2]; + } parser->tokens_avail--; } @@ -803,8 +810,12 @@ c_parser_consume_pragma (c_parser *parser) gcc_assert (parser->tokens[0].type == CPP_PRAGMA); if (parser->tokens != &parser->tokens_buf[0]) parser->tokens++; - else if (parser->tokens_avail == 2) - parser->tokens[0] = parser->tokens[1]; + else if (parser->tokens_avail >= 2) + { + parser->tokens[0] = parser->tokens[1]; + if (parser->tokens_avail >= 3) + parser->tokens[1] = parser->tokens[2]; + } parser->tokens_avail--; parser->in_pragma = true; } @@ -1384,10 +1395,15 @@ struct oacc_routine_data { location_t loc; }; +static bool c_parser_nth_token_starts_std_attributes (c_parser *, + unsigned int); +static tree c_parser_std_attribute_specifier_sequence (c_parser *); static void c_parser_external_declaration (c_parser *); static void c_parser_asm_definition (c_parser *); static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, bool, bool, tree *, vec<c_token>, + bool have_attrs = false, + tree attrs = NULL, struct oacc_routine_data * = NULL, bool * = NULL); static void c_parser_static_assert_declaration_no_semi (c_parser *); @@ -1402,10 +1418,11 @@ static struct c_declarator *c_parser_direct_declarator (c_parser *, bool, static struct c_declarator *c_parser_direct_declarator_inner (c_parser *, bool, struct c_declarator *); -static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree); +static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree, + bool); static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree, - tree); -static struct c_parm *c_parser_parameter_declaration (c_parser *, tree); + tree, bool); +static struct c_parm *c_parser_parameter_declaration (c_parser *, tree, bool); static tree c_parser_simple_asm_expr (c_parser *); static tree c_parser_gnu_attributes (c_parser *); static struct c_expr c_parser_initializer (c_parser *); @@ -1706,11 +1723,15 @@ add_debug_begin_stmt (location_t loc) declarations are OK (subject to all other constraints); otherwise (old-style parameter declarations) they are diagnosed. If START_ATTR_OK is true, the declaration specifiers may start with - attributes; otherwise they may not. + attributes (GNU or standard); otherwise they may not. OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed declaration when parsing an Objective-C foreach statement. FALLTHRU_ATTR_P is used to signal whether this function parsed - "__attribute__((fallthrough));". + "__attribute__((fallthrough));". ATTRS are any standard attributes + parsed in the caller (in contexts where such attributes had to be + parsed to determine whether what follows is a declaration or a + statement); HAVE_ATTRS says whether there were any such attributes + (even empty). declaration: declaration-specifiers init-declarator-list[opt] ; @@ -1784,6 +1805,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool nested, bool start_attr_ok, tree *objc_foreach_object_declaration, vec<c_token> omp_declare_simd_clauses, + bool have_attrs, tree attrs, struct oacc_routine_data *oacc_routine_data, bool *fallthru_attr_p) { @@ -1803,6 +1825,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } specs = build_null_declspecs (); + /* Handle any standard attributes parsed in the caller. */ + if (have_attrs) + { + declspecs_add_attrs (here, specs, attrs); + specs->non_std_attrs_seen_p = false; + } + /* Try to detect an unknown type name when we have "A B" or "A *B". */ if (c_parser_peek_token (parser)->type == CPP_NAME && c_parser_peek_token (parser)->id_kind == C_ID_ID @@ -1867,8 +1896,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, fndef_ok = !nested; } + /* When there are standard attributes at the start of the + declaration (to apply to the entity being declared), an + init-declarator-list or function definition must be present. */ + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + have_attrs = true; + c_parser_declspecs (parser, specs, true, true, start_attr_ok, - true, true, cla_nonabstract_decl); + true, true, start_attr_ok, true, cla_nonabstract_decl); if (parser->error) { c_parser_skip_to_end_of_block_or_statement (parser); @@ -1896,7 +1931,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, void_type_node, 0); add_stmt (fn); } - else if (empty_ok) + else if (empty_ok && !(have_attrs + && specs->non_std_attrs_seen_p)) shadow_tag (specs); else { @@ -2556,7 +2592,14 @@ c_parser_static_assert_declaration_no_semi (c_parser *parser) Storage class specifiers are accepted iff SCSPEC_OK; type specifiers are accepted iff TYPESPEC_OK; alignment specifiers are accepted iff ALIGNSPEC_OK; gnu-attributes are accepted at the start - iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK. + iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK. In + addition to the syntax shown, standard attributes are accepted at + the start iff START_STD_ATTR_OK and at the end iff END_STD_ATTR_OK; + unlike gnu-attributes, they are not accepted in the middle of the + list. (This combines various different syntax productions in the C + standard, and in some cases gnu-attributes and standard attributes + at the start may already have been parsed before this function is + called.) declaration-specifiers: storage-class-specifier declaration-specifiers[opt] @@ -2664,6 +2707,7 @@ void c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, bool scspec_ok, bool typespec_ok, bool start_attr_ok, bool alignspec_ok, bool auto_type_ok, + bool start_std_attr_ok, bool end_std_attr_ok, enum c_lookahead_kind la) { bool attrs_ok = start_attr_ok; @@ -2672,6 +2716,16 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, if (!typespec_ok) gcc_assert (la == cla_prefer_id); + if (start_std_attr_ok + && c_parser_nth_token_starts_std_attributes (parser, 1)) + { + gcc_assert (!specs->non_std_attrs_seen_p); + location_t loc = c_parser_peek_token (parser)->location; + tree attrs = c_parser_std_attribute_specifier_sequence (parser); + declspecs_add_attrs (loc, specs, attrs); + specs->non_std_attrs_seen_p = false; + } + while (c_parser_next_token_is (parser, CPP_NAME) || c_parser_next_token_is (parser, CPP_KEYWORD) || (c_dialect_objc () && c_parser_next_token_is (parser, CPP_LESS))) @@ -2944,7 +2998,10 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, goto out; } } - out: ; + out: + if (end_std_attr_ok + && c_parser_nth_token_starts_std_attributes (parser, 1)) + specs->postfix_attrs = c_parser_std_attribute_specifier_sequence (parser); } /* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2, C11 6.7.2.2). @@ -2967,14 +3024,16 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, enumerator-list , enumerator enumerator: - enumeration-constant - enumeration-constant = constant-expression + enumeration-constant attribute-specifier-sequence[opt] + enumeration-constant attribute-specifier-sequence[opt] + = constant-expression GNU Extensions: enumerator: - enumeration-constant gnu-attributes[opt] - enumeration-constant gnu-attributes[opt] = constant-expression + enumeration-constant attribute-specifier-sequence[opt] gnu-attributes[opt] + enumeration-constant attribute-specifier-sequence[opt] gnu-attributes[opt] + = constant-expression */ @@ -2982,12 +3041,17 @@ static struct c_typespec c_parser_enum_specifier (c_parser *parser) { struct c_typespec ret; + bool have_std_attrs; + tree std_attrs = NULL_TREE; tree attrs; tree ident = NULL_TREE; location_t enum_loc; location_t ident_loc = UNKNOWN_LOCATION; /* Quiet warning. */ gcc_assert (c_parser_next_token_is_keyword (parser, RID_ENUM)); c_parser_consume_token (parser); + have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1); + if (have_std_attrs) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); attrs = c_parser_gnu_attributes (parser); enum_loc = c_parser_peek_token (parser)->location; /* Set the location in case we create a decl now. */ @@ -3044,7 +3108,11 @@ c_parser_enum_specifier (c_parser *parser) decl_loc = value_loc = token->location; c_parser_consume_token (parser); /* Parse any specified attributes. */ - tree enum_attrs = c_parser_gnu_attributes (parser); + tree std_attrs = NULL_TREE; + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + tree enum_attrs = chainon (std_attrs, + c_parser_gnu_attributes (parser)); if (c_parser_next_token_is (parser, CPP_EQ)) { c_parser_consume_token (parser); @@ -3084,7 +3152,8 @@ c_parser_enum_specifier (c_parser *parser) } postfix_attrs = c_parser_gnu_attributes (parser); ret.spec = finish_enum (type, nreverse (values), - chainon (attrs, postfix_attrs)); + chainon (std_attrs, + chainon (attrs, postfix_attrs))); ret.kind = ctsk_tagdef; ret.expr = NULL_TREE; ret.expr_const_operands = true; @@ -3100,7 +3169,14 @@ c_parser_enum_specifier (c_parser *parser) ret.expr_const_operands = true; return ret; } - ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident); + /* Attributes may only appear when the members are defined or in + certain forward declarations (treat enum forward declarations in + GNU C analogously to struct and union forward declarations in + standard C). */ + if (have_std_attrs && c_parser_next_token_is_not (parser, CPP_SEMICOLON)) + c_parser_error (parser, "expected %<;%>"); + ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident, have_std_attrs, + std_attrs); /* In ISO C, enumerated types can be referred to only if already defined. */ if (pedantic && !COMPLETE_TYPE_P (ret.spec)) @@ -3115,9 +3191,10 @@ c_parser_enum_specifier (c_parser *parser) /* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1, C11 6.7.2.1). struct-or-union-specifier: - struct-or-union gnu-attributes[opt] identifier[opt] - { struct-contents } gnu-attributes[opt] - struct-or-union gnu-attributes[opt] identifier + struct-or-union attribute-specifier-sequence[opt] gnu-attributes[opt] + identifier[opt] { struct-contents } gnu-attributes[opt] + struct-or-union attribute-specifier-sequence[opt] gnu-attributes[opt] + identifier struct-contents: struct-declaration-list @@ -3155,6 +3232,8 @@ static struct c_typespec c_parser_struct_or_union_specifier (c_parser *parser) { struct c_typespec ret; + bool have_std_attrs; + tree std_attrs = NULL_TREE; tree attrs; tree ident = NULL_TREE; location_t struct_loc; @@ -3173,6 +3252,9 @@ c_parser_struct_or_union_specifier (c_parser *parser) } struct_loc = c_parser_peek_token (parser)->location; c_parser_consume_token (parser); + have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1); + if (have_std_attrs) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); attrs = c_parser_gnu_attributes (parser); /* Set the location in case we create a decl now. */ @@ -3291,7 +3373,9 @@ c_parser_struct_or_union_specifier (c_parser *parser) } postfix_attrs = c_parser_gnu_attributes (parser); ret.spec = finish_struct (struct_loc, type, nreverse (contents), - chainon (attrs, postfix_attrs), struct_info); + chainon (std_attrs, + chainon (attrs, postfix_attrs)), + struct_info); ret.kind = ctsk_tagdef; ret.expr = NULL_TREE; ret.expr_const_operands = true; @@ -3307,7 +3391,13 @@ c_parser_struct_or_union_specifier (c_parser *parser) ret.expr_const_operands = true; return ret; } - ret = parser_xref_tag (ident_loc, code, ident); + /* Attributes may only appear when the members are defined or in + certain forward declarations. */ + if (have_std_attrs && c_parser_next_token_is_not (parser, CPP_SEMICOLON)) + c_parser_error (parser, "expected %<;%>"); + /* ??? Existing practice is that GNU attributes are ignored after + the struct or union keyword when not defining the members. */ + ret = parser_xref_tag (ident_loc, code, ident, have_std_attrs, std_attrs); return ret; } @@ -3315,7 +3405,8 @@ c_parser_struct_or_union_specifier (c_parser *parser) *without* the trailing semicolon. struct-declaration: - specifier-qualifier-list struct-declarator-list + attribute-specifier-sequence[opt] specifier-qualifier-list + attribute-specifier-sequence[opt] struct-declarator-list static_assert-declaration-no-semi specifier-qualifier-list: @@ -3375,7 +3466,7 @@ c_parser_struct_declaration (c_parser *parser) of N1731. <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1731.pdf> */ c_parser_declspecs (parser, specs, false, true, true, - true, false, cla_nonabstract_decl); + true, false, true, true, cla_nonabstract_decl); if (parser->error) return NULL_TREE; if (!specs->declspecs_seen_p) @@ -3693,7 +3784,7 @@ c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, struct c_declarator *inner; c_parser_consume_token (parser); c_parser_declspecs (parser, quals_attrs, false, false, true, - false, false, cla_prefer_id); + false, false, true, false, cla_prefer_id); inner = c_parser_declarator (parser, type_seen_p, kind, seen_id); if (inner == NULL) return NULL; @@ -3718,14 +3809,15 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, parenthesized declarator. In an abstract declarator or parameter declarator, they could start a parenthesized declarator or a parameter list. To tell which, the open parenthesis and any - following gnu-attributes must be read. If a declaration specifier - follows, then it is a parameter list; if the specifier is a - typedef name, there might be an ambiguity about redeclaring it, - which is resolved in the direction of treating it as a typedef - name. If a close parenthesis follows, it is also an empty - parameter list, as the syntax does not permit empty abstract - declarators. Otherwise, it is a parenthesized declarator (in - which case the analysis may be repeated inside it, recursively). + following gnu-attributes must be read. If a declaration + specifier or standard attributes follow, then it is a parameter + list; if the specifier is a typedef name, there might be an + ambiguity about redeclaring it, which is resolved in the + direction of treating it as a typedef name. If a close + parenthesis follows, it is also an empty parameter list, as the + syntax does not permit empty abstract declarators. Otherwise, it + is a parenthesized declarator (in which case the analysis may be + repeated inside it, recursively). ??? There is an ambiguity in a parameter declaration "int (__attribute__((foo)) x)", where x is not a typedef name: it @@ -3758,11 +3850,18 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, *seen_id = true; inner->id_loc = c_parser_peek_token (parser)->location; c_parser_consume_token (parser); + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + { + tree std_attrs = c_parser_std_attribute_specifier_sequence (parser); + if (std_attrs) + inner = build_attrs_declarator (std_attrs, inner); + } return c_parser_direct_declarator_inner (parser, *seen_id, inner); } if (kind != C_DTR_NORMAL - && c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) + && c_parser_next_token_is (parser, CPP_OPEN_SQUARE) + && !c_parser_nth_token_starts_std_attributes (parser, 1)) { struct c_declarator *inner = build_id_declarator (NULL_TREE); inner->id_loc = c_parser_peek_token (parser)->location; @@ -3777,14 +3876,18 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, tree attrs; struct c_declarator *inner; c_parser_consume_token (parser); + bool have_gnu_attrs = c_parser_next_token_is_keyword (parser, + RID_ATTRIBUTE); attrs = c_parser_gnu_attributes (parser); if (kind != C_DTR_NORMAL && (c_parser_next_token_starts_declspecs (parser) + || (!have_gnu_attrs + && c_parser_nth_token_starts_std_attributes (parser, 1)) || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))) { struct c_arg_info *args = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL, - attrs); + attrs, have_gnu_attrs); if (args == NULL) return NULL; else @@ -3792,6 +3895,16 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, inner = build_function_declarator (args, build_id_declarator (NULL_TREE)); + if (!(args->types + && args->types != error_mark_node + && TREE_CODE (TREE_VALUE (args->types)) == IDENTIFIER_NODE) + && c_parser_nth_token_starts_std_attributes (parser, 1)) + { + tree std_attrs + = c_parser_std_attribute_specifier_sequence (parser); + if (std_attrs) + inner = build_attrs_declarator (std_attrs, inner); + } return c_parser_direct_declarator_inner (parser, *seen_id, inner); } @@ -3837,7 +3950,8 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, struct c_declarator *inner) { /* Parse a sequence of array declarators and parameter lists. */ - if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) + if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE) + && !c_parser_nth_token_starts_std_attributes (parser, 1)) { location_t brace_loc = c_parser_peek_token (parser)->location; struct c_declarator *declarator; @@ -3850,13 +3964,13 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, dimen.original_type = NULL_TREE; c_parser_consume_token (parser); c_parser_declspecs (parser, quals_attrs, false, false, true, - false, false, cla_prefer_id); + false, false, false, false, cla_prefer_id); static_seen = c_parser_next_token_is_keyword (parser, RID_STATIC); if (static_seen) c_parser_consume_token (parser); if (static_seen && !quals_attrs->declspecs_seen_p) c_parser_declspecs (parser, quals_attrs, false, false, true, - false, false, cla_prefer_id); + false, false, false, false, cla_prefer_id); if (!quals_attrs->declspecs_seen_p) quals_attrs = NULL; /* If "static" is present, there must be an array dimension. @@ -3909,6 +4023,13 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, if (declarator == NULL) return NULL; inner = set_array_declarator_inner (declarator, inner); + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + { + tree std_attrs + = c_parser_std_attribute_specifier_sequence (parser); + if (std_attrs) + inner = build_attrs_declarator (std_attrs, inner); + } return c_parser_direct_declarator_inner (parser, id_present, inner); } else if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) @@ -3916,13 +4037,26 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, tree attrs; struct c_arg_info *args; c_parser_consume_token (parser); + bool have_gnu_attrs = c_parser_next_token_is_keyword (parser, + RID_ATTRIBUTE); attrs = c_parser_gnu_attributes (parser); - args = c_parser_parms_declarator (parser, id_present, attrs); + args = c_parser_parms_declarator (parser, id_present, attrs, + have_gnu_attrs); if (args == NULL) return NULL; else { inner = build_function_declarator (args, inner); + if (!(args->types + && args->types != error_mark_node + && TREE_CODE (TREE_VALUE (args->types)) == IDENTIFIER_NODE) + && c_parser_nth_token_starts_std_attributes (parser, 1)) + { + tree std_attrs + = c_parser_std_attribute_specifier_sequence (parser); + if (std_attrs) + inner = build_attrs_declarator (std_attrs, inner); + } return c_parser_direct_declarator_inner (parser, id_present, inner); } } @@ -3930,12 +4064,16 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, } /* Parse a parameter list or identifier list, including the closing - parenthesis but not the opening one. ATTRS are the attributes at - the start of the list. ID_LIST_OK is true if an identifier list is - acceptable; such a list must not have attributes at the start. */ + parenthesis but not the opening one. ATTRS are the gnu-attributes + at the start of the list. ID_LIST_OK is true if an identifier list + is acceptable; such a list must not have attributes at the start. + HAVE_GNU_ATTRS says whether any gnu-attributes (including empty + attributes) were present (in which case standard attributes cannot + occur). */ static struct c_arg_info * -c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs) +c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs, + bool have_gnu_attrs) { push_scope (); declare_parm_level (); @@ -3988,28 +4126,31 @@ c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs) } else { - struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs, - NULL); + struct c_arg_info *ret + = c_parser_parms_list_declarator (parser, attrs, NULL, have_gnu_attrs); pop_scope (); return ret; } } /* Parse a parameter list (possibly empty), including the closing - parenthesis but not the opening one. ATTRS are the attributes at - the start of the list. EXPR is NULL or an expression that needs to - be evaluated for the side effects of array size expressions in the - parameters. */ + parenthesis but not the opening one. ATTRS are the gnu-attributes + at the start of the list; if HAVE_GNU_ATTRS, there were some such + attributes (possibly empty, in which case ATTRS is NULL_TREE), + which means standard attributes cannot start the list. EXPR is + NULL or an expression that needs to be evaluated for the side + effects of array size expressions in the parameters. */ static struct c_arg_info * -c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr) +c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr, + bool have_gnu_attrs) { bool bad_parm = false; /* ??? Following the old parser, forward parameter declarations may use abstract declarators, and if no real parameter declarations follow the forward declarations then this is not diagnosed. Also - note as above that attributes are ignored as the only contents of + note as above that gnu-attributes are ignored as the only contents of the parentheses, or as the only contents after forward declarations. */ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) @@ -4053,8 +4194,10 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr) while (true) { /* Parse a parameter. */ - struct c_parm *parm = c_parser_parameter_declaration (parser, attrs); + struct c_parm *parm = c_parser_parameter_declaration (parser, attrs, + have_gnu_attrs); attrs = NULL_TREE; + have_gnu_attrs = false; if (parm == NULL) bad_parm = true; else @@ -4064,8 +4207,11 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr) tree new_attrs; c_parser_consume_token (parser); mark_forward_parm_decls (); + bool new_have_gnu_attrs + = c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE); new_attrs = c_parser_gnu_attributes (parser); - return c_parser_parms_list_declarator (parser, new_attrs, expr); + return c_parser_parms_list_declarator (parser, new_attrs, expr, + new_have_gnu_attrs); } if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) { @@ -4103,11 +4249,14 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr) } } -/* Parse a parameter declaration. ATTRS are the attributes at the - start of the declaration if it is the first parameter. */ +/* Parse a parameter declaration. ATTRS are the gnu-attributes at the + start of the declaration if it is the first parameter; + HAVE_GNU_ATTRS is true if there were any gnu-attributes there (even + empty) there. */ static struct c_parm * -c_parser_parameter_declaration (c_parser *parser, tree attrs) +c_parser_parameter_declaration (c_parser *parser, tree attrs, + bool have_gnu_attrs) { struct c_declspecs *specs; struct c_declarator *declarator; @@ -4119,7 +4268,8 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs) while (c_parser_next_token_is (parser, CPP_PRAGMA)) c_parser_pragma (parser, pragma_param, NULL); - if (!c_parser_next_token_starts_declspecs (parser)) + if (!c_parser_next_token_starts_declspecs (parser) + && !c_parser_nth_token_starts_std_attributes (parser, 1)) { c_token *token = c_parser_peek_token (parser); if (parser->error) @@ -4161,7 +4311,7 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs) attrs = NULL_TREE; } c_parser_declspecs (parser, specs, true, true, true, true, false, - cla_nonabstract_decl); + !have_gnu_attrs, true, cla_nonabstract_decl); finish_declspecs (specs); pending_xref_error (); prefix_attrs = specs->attrs; @@ -4733,6 +4883,33 @@ c_parser_std_attribute_specifier (c_parser *parser, bool for_tm) return nreverse (attributes); } +/* Return whether standard attributes start with the Nth token. */ + +static bool +c_parser_nth_token_starts_std_attributes (c_parser *parser, unsigned int n) +{ + if (!(c_parser_peek_nth_token (parser, n)->type == CPP_OPEN_SQUARE + && c_parser_peek_nth_token (parser, n + 1)->type == CPP_OPEN_SQUARE)) + return false; + /* In C, '[[' must start attributes. In Objective-C, identifying + whether those tokens start attributes requires unbounded + lookahead, which is not yet implemented. */ + return !c_dialect_objc (); +} + +static tree +c_parser_std_attribute_specifier_sequence (c_parser *parser) +{ + tree attributes = NULL_TREE; + do + { + tree attrs = c_parser_std_attribute_specifier (parser, false); + attributes = chainon (attributes, attrs); + } + while (c_parser_nth_token_starts_std_attributes (parser, 1)); + return attributes; +} + /* Parse a type name (C90 6.5.5, C99 6.7.6, C11 6.7.7). ALIGNAS_OK says whether alignment specifiers are OK (only in cases that might be the type name of a compound literal). @@ -4749,7 +4926,7 @@ c_parser_type_name (c_parser *parser, bool alignas_ok) struct c_type_name *ret; bool dummy = false; c_parser_declspecs (parser, specs, false, true, true, alignas_ok, false, - cla_prefer_type); + false, true, cla_prefer_type); if (!specs->declspecs_seen_p) { c_parser_error (parser, "expected specifier-qualifier-list"); @@ -5281,11 +5458,18 @@ c_parser_compound_statement_nostart (c_parser *parser) { location_t loc = c_parser_peek_token (parser)->location; loc = expansion_point_location_if_in_system_header (loc); + /* Standard attributes may start a statement or a declaration. */ + bool have_std_attrs + = c_parser_nth_token_starts_std_attributes (parser, 1); + tree std_attrs = NULL_TREE; + if (have_std_attrs) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); if (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) { + c_warn_unused_attributes (std_attrs); if (c_parser_next_token_is_keyword (parser, RID_CASE)) label_loc = c_parser_peek_2nd_token (parser)->location; else @@ -5296,14 +5480,17 @@ c_parser_compound_statement_nostart (c_parser *parser) c_parser_label (parser); } else if (!last_label - && c_parser_next_tokens_start_declaration (parser)) + && (c_parser_next_tokens_start_declaration (parser) + || (have_std_attrs + && c_parser_next_token_is (parser, CPP_SEMICOLON)))) { last_label = false; mark_valid_location_for_stdc_pragma (false); bool fallthru_attr_p = false; - c_parser_declaration_or_fndef (parser, true, true, true, true, - true, NULL, vNULL, NULL, - &fallthru_attr_p); + c_parser_declaration_or_fndef (parser, true, !have_std_attrs, + true, true, true, NULL, + vNULL, have_std_attrs, std_attrs, + NULL, &fallthru_attr_p); if (last_stmt && !fallthru_attr_p) pedwarn_c90 (loc, OPT_Wdeclaration_after_statement, "ISO C90 forbids mixed declarations and code"); @@ -5315,12 +5502,17 @@ c_parser_compound_statement_nostart (c_parser *parser) /* __extension__ can start a declaration, but is also an unary operator that can start an expression. Consume all but the last of a possible series of __extension__ to - determine which. */ + determine which. If standard attributes have already + been seen, it must start a statement, not a declaration, + but standard attributes starting a declaration may appear + after __extension__. */ while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD && (c_parser_peek_2nd_token (parser)->keyword == RID_EXTENSION)) c_parser_consume_token (parser); - if (c_token_starts_declaration (c_parser_peek_2nd_token (parser))) + if (!have_std_attrs + && (c_token_starts_declaration (c_parser_peek_2nd_token (parser)) + || c_parser_nth_token_starts_std_attributes (parser, 2))) { int ext; ext = disable_extension_diagnostics (); @@ -5342,6 +5534,8 @@ c_parser_compound_statement_nostart (c_parser *parser) } else if (c_parser_next_token_is (parser, CPP_PRAGMA)) { + if (have_std_attrs) + c_parser_error (parser, "expected declaration or statement"); /* External pragmas, and some omp pragmas, are not associated with regular c code, and so are not to be considered statements syntactically. This ensures that the user doesn't put them @@ -5376,6 +5570,7 @@ c_parser_compound_statement_nostart (c_parser *parser) else { statement: + c_warn_unused_attributes (std_attrs); last_label = false; last_stmt = true; mark_valid_location_for_stdc_pragma (false); @@ -5391,11 +5586,22 @@ c_parser_compound_statement_nostart (c_parser *parser) mark_valid_location_for_stdc_pragma (save_valid_for_pragma); } -/* Parse all consecutive labels. */ +/* Parse all consecutive labels, possibly preceded by standard + attributes. In this context, a statement is required, not a + declaration, so attributes must be followed by a statement that is + not just a semicolon. */ static void c_parser_all_labels (c_parser *parser) { + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + { + tree std_attrs = c_parser_std_attribute_specifier_sequence (parser); + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + c_parser_error (parser, "expected statement"); + else + c_warn_unused_attributes (std_attrs); + } while (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) @@ -5417,7 +5623,11 @@ c_parser_all_labels (c_parser *parser) The use of gnu-attributes on labels is a GNU extension. The syntax in GNU C accepts any expressions without commas, non-constant - expressions being rejected later. */ + expressions being rejected later. Any standard + attribute-specifier-sequence before the first label has been parsed + in the caller, to distinguish statements from declarations. Any + attribute-specifier-sequence after the label is parsed in this + function. */ static void c_parser_label (c_parser *parser) @@ -5480,8 +5690,18 @@ c_parser_label (c_parser *parser) else FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p; + /* Standard attributes are only allowed here if they start a + statement, not a declaration (including the case of an + attribute-declaration with only attributes). */ + bool have_std_attrs + = c_parser_nth_token_starts_std_attributes (parser, 1); + tree std_attrs = NULL_TREE; + if (have_std_attrs) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + /* Allow '__attribute__((fallthrough));'. */ - if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) + if (!have_std_attrs + && c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) { location_t loc = c_parser_peek_token (parser)->location; tree attrs = c_parser_gnu_attributes (parser); @@ -5502,7 +5722,9 @@ c_parser_label (c_parser *parser) warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>" " can be applied to a null statement"); } - if (c_parser_next_tokens_start_declaration (parser)) + if (c_parser_next_tokens_start_declaration (parser) + || (have_std_attrs + && c_parser_next_token_is (parser, CPP_SEMICOLON))) { error_at (c_parser_peek_token (parser)->location, "a label can only be part of a statement and " @@ -5511,8 +5733,11 @@ c_parser_label (c_parser *parser) /*static_assert_ok*/ true, /*empty_ok*/ true, /*nested*/ true, /*start_attr_ok*/ true, NULL, - vNULL); + vNULL, have_std_attrs, std_attrs); } + else if (std_attrs) + /* Nonempty attributes on the following statement are ignored. */ + c_warn_unused_attributes (std_attrs); } } @@ -5520,17 +5745,18 @@ c_parser_label (c_parser *parser) statement: labeled-statement - compound-statement + attribute-specifier-sequence[opt] compound-statement expression-statement - selection-statement - iteration-statement - jump-statement + attribute-specifier-sequence[opt] selection-statement + attribute-specifier-sequence[opt] iteration-statement + attribute-specifier-sequence[opt] jump-statement labeled-statement: - label statement + attribute-specifier-sequence[opt] label statement expression-statement: expression[opt] ; + attribute-specifier-sequence expression ; selection-statement: if-statement @@ -5550,7 +5776,7 @@ c_parser_label (c_parser *parser) GNU extensions: statement: - asm-statement + attribute-specifier-sequence[opt] asm-statement jump-statement: goto * expression ; @@ -5561,9 +5787,9 @@ c_parser_label (c_parser *parser) Objective-C: statement: - objc-throw-statement - objc-try-catch-statement - objc-synchronized-statement + attribute-specifier-sequence[opt] objc-throw-statement + attribute-specifier-sequence[opt] objc-try-catch-statement + attribute-specifier-sequence[opt] objc-synchronized-statement objc-throw-statement: @throw expression ; @@ -5572,7 +5798,7 @@ c_parser_label (c_parser *parser) OpenACC: statement: - openacc-construct + attribute-specifier-sequence[opt] openacc-construct openacc-construct: parallel-construct @@ -5595,7 +5821,7 @@ c_parser_label (c_parser *parser) OpenMP: statement: - openmp-construct + attribute-specifier-sequence[opt] openmp-construct openmp-construct: parallel-construct @@ -5654,8 +5880,8 @@ c_parser_label (c_parser *parser) Transactional Memory: statement: - transaction-statement - transaction-cancel-statement + attribute-specifier-sequence[opt] transaction-statement + attribute-specifier-sequence[opt] transaction-cancel-statement IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to @@ -5671,7 +5897,8 @@ c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) } /* Parse a statement, other than a labeled statement. CHAIN is a vector - of if-else-if conditions. + of if-else-if conditions. All labels and standard attributes have + been parsed in the caller. IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to @@ -6394,7 +6621,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, c_parser_consume_token (parser); c_finish_expr_stmt (loc, NULL_TREE); } - else if (c_parser_next_tokens_start_declaration (parser)) + else if (c_parser_next_tokens_start_declaration (parser) + || c_parser_nth_token_starts_std_attributes (parser, 1)) { c_parser_declaration_or_fndef (parser, true, true, true, true, true, &object_expression, vNULL); @@ -6421,7 +6649,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, && (c_parser_peek_2nd_token (parser)->keyword == RID_EXTENSION)) c_parser_consume_token (parser); - if (c_token_starts_declaration (c_parser_peek_2nd_token (parser))) + if (c_token_starts_declaration (c_parser_peek_2nd_token (parser)) + || c_parser_nth_token_starts_std_attributes (parser, 2)) { int ext; ext = disable_extension_diagnostics (); @@ -10983,7 +11212,7 @@ c_parser_objc_method_decl (c_parser *parser, bool is_class_method, (parser, attributes) ; break; } - parm = c_parser_parameter_declaration (parser, NULL_TREE); + parm = c_parser_parameter_declaration (parser, NULL_TREE, false); if (parm == NULL) break; parms = chainon (parms, @@ -11148,7 +11377,7 @@ c_parser_objc_try_catch_finally_statement (c_parser *parser) { /* We have "@catch (NSException *exception)" or something like that. Parse the parameter declaration. */ - parm = c_parser_parameter_declaration (parser, NULL_TREE); + parm = c_parser_parameter_declaration (parser, NULL_TREE, false); if (parm == NULL) parameter_declaration = error_mark_node; else @@ -16490,12 +16719,12 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context) while (c_parser_next_token_is (parser, CPP_KEYWORD) && c_parser_peek_token (parser)->keyword == RID_EXTENSION); c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, vNULL, &data); + NULL, vNULL, false, NULL, &data); restore_extension_diagnostics (ext); } else c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, vNULL, &data); + NULL, vNULL, false, NULL, &data); } } diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h index 641da2f..b32daab 100644 --- a/gcc/c/c-parser.h +++ b/gcc/c/c-parser.h @@ -190,7 +190,8 @@ extern struct c_declarator * c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, bool *seen_id); extern void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool, - bool, bool, bool, enum c_lookahead_kind); + bool, bool, bool, bool, bool, + enum c_lookahead_kind); extern struct c_type_name *c_parser_type_name (c_parser *, bool = false); #endif diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 71cd77d..67c8f45 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -186,9 +186,13 @@ enum c_typespec_kind { kind of tag, in which case this is only valid if shadowing that tag in an inner scope. */ ctsk_tagref, + /* Likewise, with standard attributes present in the reference. */ + ctsk_tagref_attrs, /* A reference to a tag, not previously declared in a visible scope. */ ctsk_tagfirstref, + /* Likewise, with standard attributes present in the reference. */ + ctsk_tagfirstref_attrs, /* A definition of a tag such as "struct foo { int a; }". */ ctsk_tagdef, /* A typedef name. */ @@ -311,10 +315,15 @@ struct c_declspecs { tree expr; /* The attributes from a typedef decl. */ tree decl_attr; - /* When parsing, the attributes. Outside the parser, this will be - NULL; attributes (possibly from multiple lists) will be passed - separately. */ + /* When parsing, the GNU attributes and prefix standard attributes. + Outside the parser, this will be NULL; attributes (possibly from + multiple lists) will be passed separately. */ tree attrs; + /* When parsing, postfix standard attributes (which appertain to the + type specified by the preceding declaration specifiers, unlike + prefix standard attributes which appertain to the declaration or + declarations as a whole). */ + tree postfix_attrs; /* The pass to start compiling a __GIMPLE or __RTL function with. */ char *gimple_or_rtl_pass; /* ENTRY BB count. */ @@ -335,13 +344,17 @@ struct c_declspecs { ENUM_BITFIELD (c_typespec_keyword) typespec_word : 8; /* The kind of type specifier if one has been seen, ctsk_none otherwise. */ - ENUM_BITFIELD (c_typespec_kind) typespec_kind : 3; + ENUM_BITFIELD (c_typespec_kind) typespec_kind : 4; ENUM_BITFIELD (c_declspec_il) declspec_il : 3; /* Whether any expressions in typeof specifiers may appear in constant expressions. */ BOOL_BITFIELD expr_const_operands : 1; /* Whether any declaration specifiers have been seen at all. */ BOOL_BITFIELD declspecs_seen_p : 1; + /* Whether any declaration specifiers other than standard attributes + have been seen at all. If only standard attributes have been + seen, this is an attribute-declaration. */ + BOOL_BITFIELD non_std_attrs_seen_p : 1; /* Whether something other than a storage class specifier or attribute has been seen. This is used to warn for the obsolescent usage of storage class specifiers other than at the @@ -582,6 +595,7 @@ extern struct c_declarator *set_array_declarator_inner (struct c_declarator *, extern tree c_builtin_function (tree); extern tree c_builtin_function_ext_scope (tree); extern tree c_simulate_builtin_function_decl (tree); +extern void c_warn_unused_attributes (tree); extern void shadow_tag (const struct c_declspecs *); extern void shadow_tag_warned (const struct c_declspecs *, int); extern tree start_enum (location_t, struct c_enum_contents *, tree); @@ -595,7 +609,8 @@ extern void store_parm_decls_from (struct c_arg_info *); extern void temp_store_parm_decls (tree, tree); extern void temp_pop_parm_decls (void); extern tree xref_tag (enum tree_code, tree); -extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree); +extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree, + bool, tree); extern struct c_parm *build_c_parm (struct c_declspecs *, tree, struct c_declarator *, location_t); extern struct c_declarator *build_attrs_declarator (tree, diff --git a/gcc/c/gimple-parser.c b/gcc/c/gimple-parser.c index e81e523..6fdb83c 100644 --- a/gcc/c/gimple-parser.c +++ b/gcc/c/gimple-parser.c @@ -2014,7 +2014,7 @@ c_parser_gimple_declaration (gimple_parser &parser) struct c_declarator *declarator; struct c_declspecs *specs = build_null_declspecs (); c_parser_declspecs (parser, specs, true, true, true, - true, true, cla_nonabstract_decl); + true, true, true, true, cla_nonabstract_decl); finish_declspecs (specs); /* Provide better error recovery. Note that a type name here is usually diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5b42fc4..1a03eec 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2019-11-14 Joseph Myers <joseph@codesourcery.com> + + * gcc.dg/c2x-attr-fallthrough-1.c, gcc.dg/c2x-attr-syntax-1.c, + gcc.dg/c2x-attr-syntax-2.c, gcc.dg/c2x-attr-syntax-3.c, + gcc.dg/gnu2x-attr-syntax-1.c, gcc.dg/gnu2x-attr-syntax-2.c, + gcc.dg/gnu2x-attrs-1.c: New tests. + 2019-11-14 Feng Xue <fxue@os.amperecomputing.com> PR ipa/91682 diff --git a/gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c b/gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c new file mode 100644 index 0000000..ffa5226 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c @@ -0,0 +1,21 @@ +/* Test C2x attribute syntax. Valid use of fallthrough attribute. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors -Wextra" } */ + +int +f (int a) +{ + int b = 2; + switch (a) + { + case 1: + b = 1; /* { dg-warning "may fall through" } */ + case 2: + b = 2; + [[fallthrough]]; + case 3: + b += 7; + break; + } + return b; +} diff --git a/gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c b/gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c new file mode 100644 index 0000000..48e2591 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c @@ -0,0 +1,55 @@ +/* Test C2x attribute syntax. Basic tests of valid uses of empty + attributes. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +[ [ ] ] [[]]; + +[[]] int [[]] a [[]] = 123; + +int f([[]] int x [[]], [[]] long [[]], short [[]] *[[]] [3] [[]], + int [[]] (int)[[]], int (*)(int)[[]]) [[]] [[]]; + +int g [[]] [2] [[]] [3] [[]]; + +int *[[]] const *[[]] volatile *[[]] *const p; + +int *[[]][[]] q = 0; + +struct [[]] s; +union [[]][[]] u; + +struct [[]] s2 { [[]] long [[]] *a[[]] [3] [[]] [4], b[[]]; }; + +union [[]] u2 { [[]] long [[]] *a[[]] [3] [[]] [4]; }; + +int z = sizeof (int [[]]); + +enum [[]] { E1 [[]][[]], E2[[]][[]] = 3 }; +enum [[]] e { E3 = 4, E4 [[]] }; + +void +func (void) [[]] +{ + [[]] int var; + [[]] { } + [[]] switch (a) { [[]] case 1: [[]] case 2: [[]] default: [[]] var = 3; } + [[]] x : [[]] y: [[]] var = 1; + [[]]; + int [[]] var2; + [[]] if (a) [[]] (void) 0; else [[]] (void) 1; + [[]] while (0) [[]] var = 2; + [[]] do [[]] var = 3; while (0); + for ([[]] int zz = 1; zz < 10; zz++) + { + [[]] var2 = 8; + [[]] continue; + [[]] break; + } + if (a) [[]] goto x; + [[]] return; +} + +void func2 () [[]]; + +void func3 () [[]] { } diff --git a/gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c b/gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c new file mode 100644 index 0000000..ceca950 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c @@ -0,0 +1,60 @@ +/* Test C2x attribute syntax. Test ignored attributes diagnosed. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +/* A GNU attribute that is valid in some contexts, but should be + diagnosed in contexts where all attributes are ignored (attribute + declarations, except for fallthrough attributes, and + statements). */ +#define CTX [[gnu::const]] + +/* An attribute that is unknown, so ignored with a warning. */ +#define UNK [[gnu::no_such_attribute(![!(!)!]!,;;)]] + +CTX; /* { dg-warning "ignored" } */ +UNK; /* { dg-warning "ignored" } */ + +UNK extern int a; /* { dg-warning "ignored" } */ +extern int UNK a; /* { dg-warning "ignored" } */ +extern int a UNK; /* { dg-warning "ignored" } */ + +int f () UNK; /* { dg-warning "ignored" } */ +int f (void) UNK; /* { dg-warning "ignored" } */ +int g (UNK int a); /* { dg-warning "ignored" } */ +int g (int UNK a); /* { dg-warning "ignored" } */ +int g (int a UNK); /* { dg-warning "ignored" } */ +int g (UNK int); /* { dg-warning "ignored" } */ +int g (int UNK); /* { dg-warning "ignored" } */ +int g (int) UNK; /* { dg-warning "ignored" } */ + +int *UNK p; /* { dg-warning "ignored" } */ +int b[3] UNK; /* { dg-warning "ignored" } */ + +int h (int () UNK); /* { dg-warning "ignored" } */ + +struct UNK s; /* { dg-warning "ignored" } */ +union UNK u; /* { dg-warning "ignored" } */ + +struct UNK s2 { int a; }; /* { dg-warning "ignored" } */ +union UNK u2 { int a; }; /* { dg-warning "ignored" } */ + +struct s3 { UNK int a; }; /* { dg-warning "ignored" } */ +struct s4 { int UNK a; }; /* { dg-warning "ignored" } */ +union u3 { UNK int a; }; /* { dg-warning "ignored" } */ +union u4 { int UNK a; }; /* { dg-warning "ignored" } */ + +int z = sizeof (int UNK); /* { dg-warning "ignored" } */ + +enum UNK { E1 }; /* { dg-warning "ignored" } */ +enum { E2 UNK }; /* { dg-warning "ignored" } */ +enum { E3 UNK = 4 }; /* { dg-warning "ignored" } */ + +void +func (void) UNK { /* { dg-warning "ignored" } */ + UNK int var; /* { dg-warning "ignored" } */ + CTX { } /* { dg-warning "ignored" } */ + CTX; /* { dg-warning "ignored" } */ + CTX var = 1; /* { dg-warning "ignored" } */ + CTX x: var = 2; /* { dg-warning "ignored" } */ + for (UNK int zz = 1; zz < 10; zz++) ; /* { dg-warning "ignored" } */ +} diff --git a/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c b/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c new file mode 100644 index 0000000..1f883d8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c @@ -0,0 +1,56 @@ +/* Test C2x attribute syntax. Invalid uses of attributes. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +/* Prefix attributes not allowed on declarations without declarators. */ + +[[]] struct s { int a; }; /* { dg-error "empty declaration" } */ + +[[]] union u { int a; }; /* { dg-error "empty declaration" } */ + +void +f1 (void) +{ + [[]] struct t { int a; }; /* { dg-error "empty declaration" } */ +} + +/* Prefix attributes not allowed on _Static_assert. */ + +[[]] _Static_assert (1); /* { dg-error "expected" } */ + +void +f2 (void) +{ + [[]] _Static_assert (1); /* { dg-error "expected" } */ +} + +/* Declarations, including attribute declarations, cannot appear after + labels. */ + +void +f3 (void) +{ + x: [[]];; /* { dg-error "can only be part of a statement" } */ +} + +/* Prefix attributes cannot appear on type names. */ + +int z = sizeof ([[]] int); /* { dg-error "expected" } */ + +/* Attributes are not allowed after struct, union or enum, except when + the type contents are being defined or the declaration is just + "struct-or-union atribute-specifier-sequence identifier;". */ + +const struct [[]] s2; /* { dg-warning "useless type qualifier" } */ +/* { dg-error "invalid use of attributes in empty declaration" "invalid" { target *-*-* } .-1 } */ + +const union [[]] u2; /* { dg-warning "useless type qualifier" } */ +/* { dg-error "invalid use of attributes in empty declaration" "invalid" { target *-*-* } .-1 } */ + +struct [[]] s3 *sv; /* { dg-error "expected" } */ + +union [[]] u3 *uv; /* { dg-error "expected" } */ + +enum e { E1 }; + +enum [[]] e *ev; /* { dg-error "expected" } */ diff --git a/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c b/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c new file mode 100644 index 0000000..bd21086 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c @@ -0,0 +1,16 @@ +/* Test C2x attribute syntax. Basic tests of valid uses of empty + attributes with GNU C features. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2x" } */ + +/* Attributes can be used in declarations after __extension__, and + before asm statements. */ + +__extension__ [[]] int a; + +void +f (void) +{ + __extension__ [[]] int b; + [[]] asm (""); +} diff --git a/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c b/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c new file mode 100644 index 0000000..97d1654 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c @@ -0,0 +1,16 @@ +/* Test C2x attribute syntax. Invalid uses of attributes with GNU C + features. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2x -w" } */ + +/* Attributes cannot be used as prefix attributes on old-style + parameter declarations or on function declarators with identifier + lists (removed from C2x). */ + +void (*f(a, b) [[]])() int a, b; { } /* { dg-error "expected" } */ + +void f(x, y) int x; [[]] int y; { } /* { dg-error "expected" } */ + +/* Nonempty attributes cannot be used as postfix attributes with + __auto_type. */ +__auto_type [[gnu::no_such_attr]] x = 1; /* { dg-error "'__auto_type' followed by" } */ diff --git a/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c b/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c new file mode 100644 index 0000000..df22fb3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c @@ -0,0 +1,72 @@ +/* Test C2x attribute syntax. Test GNU attributes appertain to + appropriate constructs. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2x" } */ + +void f (void) {}; + +[[gnu::alias("f")]] void g (void); + +void [[gnu::alias("f")]] h (void); /* { dg-warning "ignored" } */ +/* { dg-message "that appertains to a type-specifier" "appertains" { target *-*-* } .-1 } */ + +struct [[gnu::packed]] s { int a; char b; }; +_Static_assert (sizeof (struct s) == (sizeof (int) + sizeof (char))); + +int +f2 (void) +{ + [[gnu::deprecated]] int a = 1; + return a; /* { dg-warning "deprecated" } */ +} + +int +f3 (void) +{ + int a [[gnu::deprecated]] = 1; + return a; /* { dg-warning "deprecated" } */ +} + +struct s2 { [[gnu::deprecated]] int a; int b [[gnu::deprecated]]; } x; + +int +f4 (void) +{ + return x.a; /* { dg-warning "deprecated" } */ +} + +int +f5 (void) +{ + return x.b; /* { dg-warning "deprecated" } */ +} + +enum e { E1 [[gnu::deprecated]] }; + +enum e +f6 (void) +{ + return E1; /* { dg-warning "deprecated" } */ +} + +int +f7 ([[gnu::deprecated]] int y) +{ + return y; /* { dg-warning "deprecated" } */ +} + +union [[gnu::deprecated]] u { int x; }; + +void +f8 (void) +{ + union u var; /* { dg-warning "deprecated" } */ +} + +enum [[gnu::deprecated]] edep { E2 }; + +void +f9 (void) +{ + enum edep var; /* { dg-warning "deprecated" } */ +} |