From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from esa3.mentor.iphmx.com (esa3.mentor.iphmx.com [68.232.137.180]) by sourceware.org (Postfix) with ESMTPS id D9F033858D32 for ; Thu, 3 Nov 2022 02:55:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D9F033858D32 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=codesourcery.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mentor.com X-IronPort-AV: E=Sophos;i="5.95,235,1661846400"; d="scan'208";a="85812134" Received: from orw-gwy-01-in.mentorg.com ([192.94.38.165]) by esa3.mentor.iphmx.com with ESMTP; 02 Nov 2022 18:55:32 -0800 IronPort-SDR: LSfiTmdCYFeuU9zzomwKJLPsC8wOoET5GSP/KVr3Twxc9vCIs6fGrVaPJ6HGH3PrG3cU+yEOj8 lVTudMlu7m3Kx3UxblZLOYxPjTD6LOtjJXclig49K1uqWAQuRjAgnbU0FfYd+o0lK3NAg97sNw /2XyVLBTCTUw2/NZsR8OpbgVWhQeI8o5FNB6ybjbBCpnJzEysLl9UD2DAHTrm/VIDdOaVrsFUC wzElY2NasCdT35qc8siZvmFM6H2NmlPau6B9kAYqBEQbmZ2LlFrSdPiZtVoR8vSxzA4lxTiyon x68= Date: Thu, 3 Nov 2022 02:55:25 +0000 From: Joseph Myers To: Subject: [committed] c: C2x auto Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset="US-ASCII" X-Originating-IP: [137.202.0.90] X-ClientProxiedBy: svr-ies-mbx-13.mgc.mentorg.com (139.181.222.13) To svr-ies-mbx-10.mgc.mentorg.com (139.181.222.10) X-Spam-Status: No, score=-3115.5 required=5.0 tests=BAYES_00,GIT_PATCH_0,HEADER_FROM_DIFFERENT_DOMAINS,KAM_DMARC_STATUS,KAM_STOCKGEN,SPF_HELO_PASS,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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. diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 795c97134f2..a99b7456055 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -1472,6 +1472,67 @@ pop_file_scope (void) maybe_apply_pending_pragma_weaks (); } +/* Whether we are curently inside the initializer for an + underspecified object definition (C2x auto or constexpr). */ +static bool in_underspecified_init; + +/* Start an underspecified object definition for NAME at LOC. This + means that NAME is shadowed inside its initializer, so neither the + definition being initialized, nor any definition from an outer + scope, may be referenced during that initializer. Return state to + be passed to finish_underspecified_init. */ +unsigned int +start_underspecified_init (location_t loc, tree name) +{ + bool prev = in_underspecified_init; + bool ok; + tree decl = build_decl (loc, VAR_DECL, name, error_mark_node); + C_DECL_UNDERSPECIFIED (decl) = 1; + struct c_scope *scope = current_scope; + struct c_binding *b = I_SYMBOL_BINDING (name); + if (b && B_IN_SCOPE (b, scope)) + { + error_at (loc, "underspecified declaration of %qE, which is already " + "declared in this scope", name); + ok = false; + } + else + { + bind (name, decl, scope, false, false, loc); + ok = true; + } + in_underspecified_init = true; + return ok | (prev << 1); +} + +/* Finish an underspecified object definition for NAME, before that + name is bound to the real declaration instead of a placeholder. + PREV_STATE is the value returned by the call to + start_underspecified_init. */ +void +finish_underspecified_init (tree name, unsigned int prev_state) +{ + if (prev_state & 1) + { + /* A VAR_DECL was bound to the name to shadow any previous + declarations for the name; remove that binding now. */ + struct c_scope *scope = current_scope; + struct c_binding *b = I_SYMBOL_BINDING (name); + gcc_assert (b); + gcc_assert (B_IN_SCOPE (b, scope)); + gcc_assert (VAR_P (b->decl)); + gcc_assert (C_DECL_UNDERSPECIFIED (b->decl)); + I_SYMBOL_BINDING (name) = b->shadowed; + /* In erroneous cases there may be other bindings added to this + scope during the initializer. */ + struct c_binding **p = &scope->bindings; + while (*p != b) + p = &((*p)->prev); + *p = free_binding_and_advance (*p); + } + in_underspecified_init = (prev_state & (1u << 1)) >> 1; +} + /* Adjust the bindings for the start of a statement expression. */ void @@ -4764,6 +4825,17 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) warned = 1; } + if (in_underspecified_init) + { + /* This can only occur with extensions such as statement + expressions, but is still appropriate as an error to + avoid types declared in such a context escaping to + the type of an auto variable. */ + error ("%qT declared in underspecified object initializer", + value); + warned = 1; + } + if (name == NULL_TREE) { if (warned != 1 && code != ENUMERAL_TYPE) @@ -6458,6 +6530,15 @@ grokdeclarator (const struct c_declarator *declarator, enum c_declarator_kind first_non_attr_kind; unsigned int alignas_align = 0; + if (type == NULL_TREE) + { + /* This can occur for auto on a parameter in C2X mode. Set a + dummy type here so subsequent code can give diagnostics for + this case. */ + gcc_assert (declspecs->c2x_auto_p); + gcc_assert (decl_context == PARM); + type = declspecs->type = integer_type_node; + } if (TREE_CODE (type) == ERROR_MARK) return error_mark_node; if (expr == NULL) @@ -6683,9 +6764,13 @@ grokdeclarator (const struct c_declarator *declarator, || storage_class == csc_typedef) storage_class = csc_none; } - else if (decl_context != NORMAL && (storage_class != csc_none || threadp)) + else if (decl_context != NORMAL && (storage_class != csc_none + || threadp + || declspecs->c2x_auto_p)) { - if (decl_context == PARM && storage_class == csc_register) + if (decl_context == PARM + && storage_class == csc_register + && !declspecs->c2x_auto_p) ; else { @@ -8428,6 +8513,9 @@ 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); + if (in_underspecified_init) + error_at (loc, "%qT declared in underspecified object initializer", + ref); ret.spec = ref; return ret; @@ -8523,6 +8611,9 @@ start_struct (location_t loc, enum tree_code code, tree name, ? "sizeof" : (in_typeof ? "typeof" : "alignof"))); + if (in_underspecified_init) + error_at (loc, "%qT defined in underspecified object initializer", ref); + return ref; } @@ -9433,6 +9524,10 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name, ? "sizeof" : (in_typeof ? "typeof" : "alignof"))); + if (in_underspecified_init) + error_at (loc, "%qT defined in underspecified object initializer", + enumtype); + return enumtype; } @@ -11261,6 +11356,20 @@ declspecs_add_type (location_t loc, struct c_declspecs *specs, if (TREE_UNAVAILABLE (type)) specs->unavailable_p = true; + /* As a type specifier is present, "auto" must be used as a storage + class specifier, not for type deduction. */ + if (specs->c2x_auto_p) + { + specs->c2x_auto_p = false; + if (specs->storage_class != csc_none) + error ("multiple storage classes in declaration specifiers"); + else if (specs->thread_p) + error ("%qs used with %", + specs->thread_gnu_p ? "__thread" : "_Thread_local"); + else + specs->storage_class = csc_auto; + } + /* Handle type specifier keywords. */ if (TREE_CODE (type) == IDENTIFIER_NODE && C_IS_RESERVED_WORD (type) @@ -12179,6 +12288,16 @@ declspecs_add_scspec (location_t loc, } break; case RID_AUTO: + if (flag_isoc2x + && specs->typespec_kind == ctsk_none + && specs->storage_class != csc_typedef) + { + /* "auto" potentially used for type deduction. */ + if (specs->c2x_auto_p) + error ("duplicate %qE", scspec); + specs->c2x_auto_p = true; + return specs; + } n = csc_auto; break; case RID_EXTERN: @@ -12198,6 +12317,11 @@ declspecs_add_scspec (location_t loc, break; case RID_TYPEDEF: n = csc_typedef; + if (specs->c2x_auto_p) + { + error ("% used with %"); + specs->c2x_auto_p = false; + } break; default: gcc_unreachable (); @@ -12284,7 +12408,7 @@ finish_declspecs (struct c_declspecs *specs) { gcc_assert (!specs->long_p && !specs->long_long_p && !specs->short_p && !specs->signed_p && !specs->unsigned_p - && !specs->complex_p); + && !specs->complex_p && !specs->c2x_auto_p); /* Set a dummy type. */ if (TREE_CODE (specs->type) == ERROR_MARK) @@ -12320,6 +12444,18 @@ finish_declspecs (struct c_declspecs *specs) "ISO C does not support plain % meaning " "%"); } + else if (specs->c2x_auto_p) + { + /* Type to be filled in later, including applying postfix + attributes. This warning only actually appears for + -Wc11-c2x-compat in C2X mode; in older modes, there may + be a warning or pedwarn for implicit "int" instead, or + other errors for use of auto at file scope. */ + pedwarn_c11 (input_location, OPT_Wpedantic, + "ISO C does not support % type deduction " + "before C2X"); + return specs; + } else { specs->typespec_word = cts_int; @@ -12336,6 +12472,7 @@ finish_declspecs (struct c_declspecs *specs) specs->explicit_signed_p = specs->signed_p; /* Now compute the actual type. */ + gcc_assert (!specs->c2x_auto_p); switch (specs->typespec_word) { case cts_auto_type: diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index ca533c9c667..d70697b1d63 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, + "% 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; } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index facfc125371..8116e5cc984 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -100,6 +100,10 @@ along with GCC; see the file COPYING3. If not see #define C_DECL_COMPOUND_LITERAL_P(DECL) \ DECL_LANG_FLAG_5 (VAR_DECL_CHECK (DECL)) +/* Set on decls used as placeholders for a C2x underspecified object + definition. */ +#define C_DECL_UNDERSPECIFIED(DECL) DECL_LANG_FLAG_7 (DECL) + /* Nonzero for a decl which either doesn't exist or isn't a prototype. N.B. Could be simplified if all built-in decls had complete prototypes (but this is presently difficult because some of them need FILE*). */ @@ -430,6 +434,11 @@ struct c_declspecs { enum-type-specifier;", but such an empty declaration is valid in C2x when "enum identifier;" would not be). */ BOOL_BITFIELD enum_type_specifier_ref_p : 1; + /* Whether "auto" was specified in C2X (or later) mode and means the + type is to be deduced from an initializer, or would mean that if + no type specifier appears later in these declaration + specifiers. */ + BOOL_BITFIELD c2x_auto_p : 1; /* The address space that the declaration belongs to. */ addr_space_t address_space; }; @@ -592,6 +601,8 @@ extern bool switch_statement_break_seen_p; extern bool global_bindings_p (void); extern tree pushdecl (tree); +extern unsigned int start_underspecified_init (location_t, tree); +extern void finish_underspecified_init (tree, unsigned int); extern void push_scope (void); extern tree pop_scope (void); extern void c_bindings_start_stmt_expr (struct c_spot_bindings *); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 052ae6db000..636098444b4 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2861,6 +2861,9 @@ build_external_ref (location_t loc, tree id, bool fun, tree *type) { ref = decl; *type = TREE_TYPE (ref); + if (DECL_P (decl) && C_DECL_UNDERSPECIFIED (decl)) + error_at (loc, "underspecified %qD referenced in its initializer", + decl); } else if (fun) /* Implicit function declaration. */ diff --git a/gcc/testsuite/gcc.dg/c2x-auto-1.c b/gcc/testsuite/gcc.dg/c2x-auto-1.c new file mode 100644 index 00000000000..f8460fb3bfb --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-auto-1.c @@ -0,0 +1,81 @@ +/* Test C2x auto. Valid code, compilation tests. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +auto i = 1; +extern int i; +static auto l = { 0L }; +extern long l; +extern auto const d = 0.0; /* { dg-warning "initialized and declared 'extern'" } */ +extern const double d; +double dx; +auto ((i2)) = 3; +extern int i2; +const auto i3 [[]] = { 4, }; +extern int i4; +thread_local auto f = 1.0f; +float ff; +extern typeof (f) ff; +auto h = (short) 0; +extern short h; + +struct s { int a; }; +struct s sv; +struct s2; +enum e : int; + +extern const volatile long double cvld; +extern void (*tfp) (void); + +int a[10]; +int *ap; + +typedef int ti; + +void +tf () +{ + auto asv = (struct s) { 0 }; + extern typeof (asv) sv; + auto s2p = (struct s2 *) 0; + struct s3; + auto s3p = (struct s3 *) 0; + auto ev = (enum e) 0; + static const auto volatile acvld = 0.5L; + extern typeof (acvld) cvld; + /* lvalue conversion occurs on the initializer, so losing qualifiers. */ + auto ncd = d; + extern typeof (ncd) dx; + _Atomic double ad = 0.0; + auto nad = ad; + extern typeof (nad) dx; + /* Function-to-pointer conversion occurs on the initializer. */ + auto fp = tf; + extern typeof (fp) tfp; + /* Array-to-pointer conversion occurs on the initializer. */ + auto aap = a; + extern typeof (aap) ap; + /* Shadowing a declaration from a containing scope is OK. */ + auto i = 2L; + extern typeof (i) l; + /* auto can be used in for loops. */ + for (auto ix = 2; ix < 10; ix++) + { + extern typeof (ix) i2; + } + /* auto is valid with bit-field initializers; the choice of type those have + in expressions is unspecified but should match how _Generic handles such + expressions. */ + struct b { int a : 2; unsigned b : 3; } bv = { }; + auto bfa = bv.a; + auto bfb = bv.b; + static_assert (_Generic (bv.a, typeof (bfa) : 1, default : 2) == 1); + static_assert (_Generic (bv.b, typeof (bfb) : 1, default : 2) == 1); + /* The traditional meaning of auto with a type specifier is OK. */ + auto short s; + char auto c; + auto struct t { int x; } t; + /* That includes the case where the type comes from a typedef name. */ + auto ti int_from_typedef = 3.0; + extern typeof (int_from_typedef) i2; +} diff --git a/gcc/testsuite/gcc.dg/c2x-auto-2.c b/gcc/testsuite/gcc.dg/c2x-auto-2.c new file mode 100644 index 00000000000..a41f813018c --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-auto-2.c @@ -0,0 +1,38 @@ +/* Test C2x auto. Valid code, execution tests. Based on auto-type-1.c. */ +/* { dg-do run } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ +/* { dg-require-effective-target alloca } */ + +extern void abort (void); +extern void exit (int); + +auto i = 1; +extern int i; +auto c = (char) 1; +extern char c; +static auto u = 10U; +extern unsigned int u; +const auto ll = 1LL; +extern const long long ll; + +int +main (void) +{ + if (i != 1 || c != 1 || u != 10U) + abort (); + auto ai = i; + int *aip = &ai; + if (ai != 1) + abort (); + auto p = (int (*) [++i]) 0; + if (i != 2) + abort (); + if (sizeof (*p) != 2 * sizeof (int)) + abort (); + int vla[u][u]; + int (*vp)[u] = &vla[0]; + auto vpp = ++vp; + if (vp != &vla[1]) + abort (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/c2x-auto-3.c b/gcc/testsuite/gcc.dg/c2x-auto-3.c new file mode 100644 index 00000000000..a34ce31f6be --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-auto-3.c @@ -0,0 +1,64 @@ +/* Test C2x auto. Invalid code. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +auto; /* { dg-error "empty declaration" } */ +auto *p = (int *) 0; /* { dg-error "plain identifier" } */ +auto i; /* { dg-error "initialized data declaration" } */ +auto g { } /* { dg-error "initialized data declaration" } */ +auto a = 1, b = 2; /* { dg-error "single declarator" } */ +auto long e0 = 0; /* { dg-error "file-scope declaration" } */ +long auto e1 = 0; /* { dg-error "file-scope declaration" } */ +int auto e2 = 0; /* { dg-error "file-scope declaration" } */ + +extern int e3; +auto e3 = 1; /* { dg-error "underspecified declaration of 'e3', which is already declared in this scope" } */ + +void +f () +{ + extern int fe1; + auto fe1 = 1; /* { dg-error "underspecified declaration of 'fe1', which is already declared in this scope" } */ + /* { dg-error "declaration of 'fe1' with no linkage follows extern declaration" "linkage error" { target *-*-* } .-1 } */ + auto fe2 = (struct s *) 0; /* { dg-error "declared in underspecified object initializer" } */ + auto fe3 = (union u *) 0; /* { dg-error "declared in underspecified object initializer" } */ + auto fe4 = (struct s2 { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ + auto fe5 = (struct { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ + auto fe6 = (union u2 { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ + auto fe7 = (union { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ + auto fe8 = sizeof (enum e { A }); /* { dg-error "defined in underspecified object initializer" } */ + /* The following case is undefined behavior (so doesn't actually require a + diagnostic). */ + auto fe9 = sizeof (enum { B }); /* { dg-error "defined in underspecified object initializer" } */ + /* Examples with a forward declaration, then definition inside auto. */ + struct s3; + auto fe10 = (struct s3 { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ + union u3; + auto fe11 = (union u3 { int a; }) { }; /* { dg-error "defined in underspecified object initializer" } */ +} + +void f2 (auto x); /* { dg-error "storage class specified for parameter" } */ +void f3 (auto y) { } /* { dg-error "storage class specified for parameter" } */ + +auto e4 = sizeof (e4); /* { dg-error "underspecified 'e4' referenced in its initializer" } */ +__SIZE_TYPE__ e5; +void +f4 () +{ + auto e5 = sizeof (e5); /* { dg-error "underspecified 'e5' referenced in its initializer" } */ +} + +auto typedef int T; /* { dg-error "'typedef' used with 'auto'" } */ +auto auto e6 = 1; /* { dg-error "duplicate 'auto'" } */ +static auto int e7 = 1; /* { dg-error "multiple storage classes in declaration specifiers" } */ +_Thread_local auto int e8 = 2; /* { dg-error "'_Thread_local' used with 'auto'" } */ +_Thread_local int auto e9 = 3; /* { dg-error "'_Thread_local' used with 'auto'" } */ +/* { dg-error "file-scope declaration of 'e9' specifies 'auto'" "file-scope error" { target *-*-* } .-1 } */ + +typedef auto int T2; /* { dg-error "multiple storage classes in declaration specifiers" } */ + +void +f5 () +{ + static int auto e10 = 3; /* { dg-error "multiple storage classes in declaration specifiers" } */ +} diff --git a/gcc/testsuite/gcc.dg/c2x-auto-4.c b/gcc/testsuite/gcc.dg/c2x-auto-4.c new file mode 100644 index 00000000000..3c6cb34710e --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-auto-4.c @@ -0,0 +1,5 @@ +/* Test C2x auto. -Wc11-c2x-compat warning. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */ + +auto x = 2; /* { dg-warning "ISO C does not support 'auto' type deduction before" } */ diff --git a/gcc/testsuite/gcc.dg/gnu2x-auto-1.c b/gcc/testsuite/gcc.dg/gnu2x-auto-1.c new file mode 100644 index 00000000000..e0b9e867d6e --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu2x-auto-1.c @@ -0,0 +1,11 @@ +/* Test C2x auto. Invalid code with GNU extensions. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2x" } */ + +void +f () +{ + /* Do not allow a non-definition declaration of a tag in the auto + initializer, to avoid it escaping an inner scope as shown here. */ + auto x = ({ struct s; struct s *x = 0; x; }); /* { dg-error "declared in underspecified object initializer" } */ +} -- Joseph S. Myers joseph@codesourcery.com