aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-decl.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2019-11-14 03:49:43 +0000
committerJoseph Myers <jsm28@gcc.gnu.org>2019-11-14 03:49:43 +0000
commit4e03c3a7c1149a8e43b7a2bfd927945cf1e90d19 (patch)
tree52ad5537d545cae2bfef8c73f76fee3ba758da47 /gcc/c/c-decl.c
parenteb270950acbae6f70e3487a6e63a26c1294656b3 (diff)
downloadgcc-4e03c3a7c1149a8e43b7a2bfd927945cf1e90d19.zip
gcc-4e03c3a7c1149a8e43b7a2bfd927945cf1e90d19.tar.gz
gcc-4e03c3a7c1149a8e43b7a2bfd927945cf1e90d19.tar.bz2
Support C2x [[]] attributes for C.
This patch adds support for the C2x [[]] attribute syntax to the C front end. Support is only added for C at this point, not for Objective-C; I intend to add the unbounded lookahead required to support it for Objective-C in a followup patch, but maybe not in development stage 1. The syntax is supported in all relevant places where the standard says it is supported, but support is not added for the individual attributes specified in C2x (all of which are optional to support). I expect to add support for some of them in followup patches; all except nodiscard can be mapped directly to the semantics of an existing GNU attribute (subject to extra checks for invalid usages such as the same attribute being used more than once inside a single [[]]), and the fallthrough attribute already works after this patch because of existing special-case code handling it (but without some of the checks for invalid usage being present). Note that the four functions c_token_starts_declspecs, c_token_starts_declaration, c_parser_next_token_starts_declspecs and c_parser_next_tokens_start_declaration do *not* accept "[[". This is analogous with the handling of __extension__: both cases have the property that they can start either a declaration or some other statements and so need an unbounded number of tokens to be parsed in the caller before it can find out what kind of syntactic construct follows. Note also that, while I updated all places calling those functions for standard C syntax to handle attributes if applicable, I did not do anything regarding calls to such functions for OpenMP or OpenACC constructs. Thus, if there are such constructs using such functions where "[[" *should* be accepted as a possible start to a declaration, the code for parsing those constructs should be updated accordingly. Although all cases of the syntax are handled, and attributes applied to the constructs the standard says they should be (with less laxity than there is for GNU attributes to allow an attribute applied to one construct to be moved automatically to another one), there is a major limitation in the existing language-independent code in attribs.c preventing most cases of type attributes from working. The following code has been present with minor changes since the first support for [[]] attributes for C++ was added: if (TYPE_P (*node) && cxx11_attr_p && !(flags & ATTR_FLAG_TYPE_IN_PLACE)) { /* This is a c++11 attribute that appertains to a type-specifier, outside of the definition of, a class type. Ignore it. */ auto_diagnostic_group d; if (warning (OPT_Wattributes, "attribute ignored")) inform (input_location, "an attribute that appertains to a type-specifier " "is ignored"); continue; } I see no justification for this in general for either C or C++ and so propose to remove or restrict it in a followup bug-fix patch. Both C and C++ are clear about attributes in certain places (at the end of declaration specifiers, or after function or array declarators) appertaining to a specific type (and explicitly say, in the case of attributes at the end of declaration specifiers, that they only apply for that particular use of that type, not for subsequent uses of the same type without the attributes). Thus it seems clear to me that, for example, int [[gnu::mode(DI)]] x; ought to be accepted as an analogue in [[]] syntax for int __attribute__((mode(DI))) x; (or strictly as an analogue for a version of that with extra parentheses to make the GNU attribute bind properly to the type rather than being automatically moved from the declaration to the type). There are certain cases where an attribute *does* only make sense for the definition of a type (e.g. "packed" on structure types), but those should already be handled in the individual attribute handlers (such as handle_packed_attribute, which already has code to deal with that issue). So my inclination is that the above-quoted check in attribs.c should simply be removed, but failing that it should be restricted to structure and union types (and such a change would be a bug-fix). That would then allow various cases of [[]] attributes on types to work properly. Bootstrapped with no regressions on x86_64-pc-linux-gnu. gcc/c: * 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. gcc/testsuite: * 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. From-SVN: r278194
Diffstat (limited to 'gcc/c/c-decl.c')
-rw-r--r--gcc/c/c-decl.c110
1 files changed, 93 insertions, 17 deletions
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;
}