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));
+}
\f
/* 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
}
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)
}
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
}
else if (declspecs->typespec_kind != ctsk_tagdef
&& declspecs->typespec_kind != ctsk_tagfirstref
+ && declspecs->typespec_kind != ctsk_tagfirstref_attrs
&& declspecs->alignas_p)
{
if (warned != 1)
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");
}
}
{
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++");
}
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:
\f
/* 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;
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)
}
pushtag (loc, name, ref);
+ decl_attributes (&ref, attrs, (int) ATTR_FLAG_TYPE_IN_PLACE);
ret.spec = ref;
return ret;
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;
}
\f
/* Make sure that the tag NAME is defined *in the current scope*
{
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)
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);
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;
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);
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;
}
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)
&& !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
default:
gcc_unreachable ();
}
+ if (specs->type != NULL)
+ {
+ decl_attributes (&specs->type, specs->postfix_attrs, 0);
+ specs->postfix_attrs = NULL_TREE;
+ }
return specs;
}
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)
{
}
-/* 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)
{
}
/* 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)
{
}
/* 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)
{
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--;
}
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;
}
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 *);
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 *);
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] ;
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)
{
}
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
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);
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
{
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]
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;
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)))
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).
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
*/
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. */
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);
}
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;
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))
/* 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
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;
}
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. */
}
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;
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;
}
*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:
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)
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;
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
*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;
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
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);
}
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;
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.
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))
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);
}
}
}
/* 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 ();
}
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))
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
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))
{
}
}
-/* 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;
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)
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;
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).
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");
{
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
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");
/* __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 ();
}
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
else
{
statement:
+ c_warn_unused_attributes (std_attrs);
last_label = false;
last_stmt = true;
mark_valid_location_for_stdc_pragma (false);
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)
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)
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);
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 "
/*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);
}
}
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
GNU extensions:
statement:
- asm-statement
+ attribute-specifier-sequence[opt] asm-statement
jump-statement:
goto * expression ;
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 ;
OpenACC:
statement:
- openacc-construct
+ attribute-specifier-sequence[opt] openacc-construct
openacc-construct:
parallel-construct
OpenMP:
statement:
- openmp-construct
+ attribute-specifier-sequence[opt] openmp-construct
openmp-construct:
parallel-construct
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
}
/* 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
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);
&& (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 ();
(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,
{
/* 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
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);
}
}