diff options
author | Joseph Myers <joseph@codesourcery.com> | 2022-11-03 02:53:41 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2022-11-03 02:53:41 +0000 |
commit | e3c898e12d0ace542498c9fe975e549b5a6c8acd (patch) | |
tree | 190e2caa19546fbe02ae5501d17390a218a4a148 /gcc/c/c-parser.cc | |
parent | 58035eeece7894d1936db6bf0dd4f8eedd07a479 (diff) | |
download | gcc-e3c898e12d0ace542498c9fe975e549b5a6c8acd.zip gcc-e3c898e12d0ace542498c9fe975e549b5a6c8acd.tar.gz gcc-e3c898e12d0ace542498c9fe975e549b5a6c8acd.tar.bz2 |
c: C2x auto
Implement C2x auto, a more restricted version of the C++ feature
(closer to GNU C __auto_type in terms of what's supported).
Since the feature is very close to GNU C __auto_type, much of the
implementation can be shared. The main differences are:
* Any prior declaration of the identifier in an outer scope is
shadowed during the initializer (whereas __auto_type leaves any such
declaration visible until the initializer ends and the scope of the
__auto_type declaration itself starts). (A prior declaration in the
same scope is undefined behavior.)
* The standard feature supports braced initializers (containing a
single expression, optionally followed by a comma).
* The standard feature disallows the declaration from declaring
anything that's not an ordinary identifier (thus, the initializer
cannot declare a tag or the members of a structure or union), while
making it undefined behavior for it to declare more than one
ordinary identifier. (For the latter, while I keep the existing
error from __auto_type in the case of more than one declarator, I
don't restrict other ordinary identifiers from being declared in
inner scopes such as GNU statement expressions. I do however
disallow defining the members of an enumeration inside the
initializer (if the enum definition has no tag, that doesn't
actually violate a constraint), to avoid an enum type becoming
accessible beyond where it would have been without auto.
(Preventing new types from escaping the initializer - thus, ensuring
that anything written with auto corresponds to something that could
have been written without auto, modulo multiple evaluation of VLA
size expressions when not using auto - is a key motivation for some
restrictions on what can be declared in the initializer.)
The rule on shadowing and restrictions on other declarations in the
initializer are actually general rules for what C2x calls
underspecified declarations, a description that covers constexpr as
well as auto (in particular, this disallows a constexpr initializer
from referencing the variable being initialized). Thus, some of the
code added for those restrictions will also be of use in implementing
C2x constexpr.
auto with a type specifier remains a storage class specifier with the
same meaning as before (i.e. a redundant storage class specifier for
use at block scope).
Note that the feature is only enabled in C2x mode (-std=c2x or
-std=gnu2x); in older modes, a declaration with auto and no type is
treated as a case of implicit int (only accepted at block scope).
Since many of the restrictions on C2x auto are specified as undefined
behavior rather than constraint violations, it would be possible to
support more features from C++ auto without requiring diagnostics (but
maybe not a good idea, if it isn't clear exactly what semantics might
be given to such a feature in a future revision of C; and
-Wc23-c2y-compat should arguably warn for any such future feature
anyway). For now the features are limited to something close to
what's supported with __auto_type, with the differences as discussed
above between the two features.
Bootstrapped with no regressions for x86_64-pc-linux-gnu.
gcc/c/
* c-decl.cc (in_underspecified_init, start_underspecified_init)
(finish_underspecified_init): New.
(shadow_tag_warned, parser_xref_tag, start_struct, start_enum):
Give errors inside initializers of underspecified declarations.
(grokdeclarator): Handle (erroneous) case of C2X auto on a
parameter.
(declspecs_add_type): Handle c2x_auto_p case.
(declspecs_add_scspec): Handle auto possibly setting c2x_auto_p in
C2X mode.
(finish_declspecs): Handle c2x_auto_p.
* c-parser.cc (c_parser_declaration_or_fndef): Handle C2X auto.
* c-tree.h (C_DECL_UNDERSPECIFIED): New macro.
(struct c_declspecs): Add c2x_auto_p.
(start_underspecified_init, finish_underspecified_init): New
prototypes.
* c-typeck.cc (build_external_ref): Give error for underspecified
declaration referenced in its initializer.
gcc/testsuite/
* gcc.dg/c2x-auto-1.c, gcc.dg/c2x-auto-2.c, gcc.dg/c2x-auto-3.c,
gcc.dg/c2x-auto-4.c, gcc.dg/gnu2x-auto-1.c: New tests.
Diffstat (limited to 'gcc/c/c-parser.cc')
-rw-r--r-- | gcc/c/c-parser.cc | 81 |
1 files changed, 65 insertions, 16 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index ca533c9..d70697b 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -2103,7 +2103,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } finish_declspecs (specs); - bool auto_type_p = specs->typespec_word == cts_auto_type; + bool gnu_auto_type_p = specs->typespec_word == cts_auto_type; + bool std_auto_type_p = specs->c2x_auto_p; + bool any_auto_type_p = gnu_auto_type_p || std_auto_type_p; + gcc_assert (!(gnu_auto_type_p && std_auto_type_p)); + const char *auto_type_keyword = gnu_auto_type_p ? "__auto_type" : "auto"; if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { bool handled_assume = false; @@ -2114,8 +2118,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, specs->attrs = handle_assume_attribute (here, specs->attrs, nested); } - if (auto_type_p) - error_at (here, "%<__auto_type%> in empty declaration"); + if (any_auto_type_p) + error_at (here, "%qs in empty declaration", auto_type_keyword); else if (specs->typespec_kind == ctsk_none && attribute_fallthrough_p (specs->attrs)) { @@ -2159,7 +2163,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, shadow_tag_warned (specs, 1); return; } - else if (c_dialect_objc () && !auto_type_p) + else if (c_dialect_objc () && !any_auto_type_p) { /* Prefix attributes are an error on method decls. */ switch (c_parser_peek_token (parser)->type) @@ -2253,6 +2257,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool dummy = false; timevar_id_t tv; tree fnbody = NULL_TREE; + tree std_auto_name = NULL_TREE; /* Declaring either one or more declarators (in which case we should diagnose if there were no declaration specifiers) or a function definition (in which case the diagnostic for @@ -2270,7 +2275,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_skip_to_end_of_block_or_statement (parser); return; } - if (auto_type_p && declarator->kind != cdk_id) + if (gnu_auto_type_p && declarator->kind != cdk_id) { error_at (here, "%<__auto_type%> requires a plain identifier" @@ -2278,6 +2283,21 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_skip_to_end_of_block_or_statement (parser); return; } + if (std_auto_type_p) + { + struct c_declarator *d = declarator; + while (d->kind == cdk_attrs) + d = d->declarator; + if (d->kind != cdk_id) + { + error_at (here, + "%<auto%> requires a plain identifier, possibly with" + " attributes, as declarator"); + c_parser_skip_to_end_of_block_or_statement (parser); + return; + } + std_auto_name = d->u.id.id; + } if (c_parser_next_token_is (parser, CPP_EQ) || c_parser_next_token_is (parser, CPP_COMMA) || c_parser_next_token_is (parser, CPP_SEMICOLON) @@ -2317,19 +2337,37 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, struct c_expr init; location_t init_loc; c_parser_consume_token (parser); - if (auto_type_p) + if (any_auto_type_p) { init_loc = c_parser_peek_token (parser)->location; rich_location richloc (line_table, init_loc); + unsigned int underspec_state = 0; + if (std_auto_type_p) + underspec_state = start_underspecified_init (init_loc, + std_auto_name); start_init (NULL_TREE, asm_name, global_bindings_p (), &richloc); /* A parameter is initialized, which is invalid. Don't attempt to instrument the initializer. */ int flag_sanitize_save = flag_sanitize; if (nested && !empty_ok) flag_sanitize = 0; - init = c_parser_expr_no_commas (parser, NULL); + if (std_auto_type_p + && c_parser_next_token_is (parser, CPP_OPEN_BRACE)) + { + matching_braces braces; + braces.consume_open (parser); + init = c_parser_expr_no_commas (parser, NULL); + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + braces.skip_until_found_close (parser); + } + else + init = c_parser_expr_no_commas (parser, NULL); + if (std_auto_type_p) + finish_underspecified_init (std_auto_name, underspec_state); flag_sanitize = flag_sanitize_save; - if (TREE_CODE (init.value) == COMPONENT_REF + if (gnu_auto_type_p + && TREE_CODE (init.value) == COMPONENT_REF && DECL_C_BIT_FIELD (TREE_OPERAND (init.value, 1))) error_at (here, "%<__auto_type%> used with a bit-field" @@ -2345,6 +2383,16 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, specs->locations[cdw_typedef] = init_loc; specs->typedef_p = true; specs->type = init_type; + if (specs->postfix_attrs) + { + /* Postfix [[]] attributes are valid with C2X + auto, although not with __auto_type, and + modify the type given by the initializer. */ + specs->postfix_attrs = + c_warn_type_attributes (specs->postfix_attrs); + decl_attributes (&specs->type, specs->postfix_attrs, 0); + specs->postfix_attrs = NULL_TREE; + } if (vm_type) { bool maybe_const = true; @@ -2400,11 +2448,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } else { - if (auto_type_p) + if (any_auto_type_p) { error_at (here, - "%<__auto_type%> requires an initialized " - "data declaration"); + "%qs requires an initialized data declaration", + auto_type_keyword); c_parser_skip_to_end_of_block_or_statement (parser); return; } @@ -2492,11 +2540,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } if (c_parser_next_token_is (parser, CPP_COMMA)) { - if (auto_type_p) + if (any_auto_type_p) { error_at (here, - "%<__auto_type%> may only be used with" - " a single declarator"); + "%qs may only be used with a single declarator", + auto_type_keyword); c_parser_skip_to_end_of_block_or_statement (parser); return; } @@ -2529,10 +2577,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, return; } } - else if (auto_type_p) + else if (any_auto_type_p) { error_at (here, - "%<__auto_type%> requires an initialized data declaration"); + "%qs requires an initialized data declaration", + auto_type_keyword); c_parser_skip_to_end_of_block_or_statement (parser); return; } |