maybe_apply_pending_pragma_weaks ();
}
\f
+/* 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;
+}
+\f
/* Adjust the bindings for the start of a statement expression. */
void
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)
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)
|| 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
{
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;
? "sizeof"
: (in_typeof ? "typeof" : "alignof")));
+ if (in_underspecified_init)
+ error_at (loc, "%qT defined in underspecified object initializer", ref);
+
return ref;
}
? "sizeof"
: (in_typeof ? "typeof" : "alignof")));
+ if (in_underspecified_init)
+ error_at (loc, "%qT defined in underspecified object initializer",
+ enumtype);
+
return enumtype;
}
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 %<auto%>",
+ 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)
}
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:
break;
case RID_TYPEDEF:
n = csc_typedef;
+ if (specs->c2x_auto_p)
+ {
+ error ("%<typedef%> used with %<auto%>");
+ specs->c2x_auto_p = false;
+ }
break;
default:
gcc_unreachable ();
{
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)
"ISO C does not support plain %<complex%> meaning "
"%<double complex%>");
}
+ 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 %<auto%> type deduction "
+ "before C2X");
+ return specs;
+ }
else
{
specs->typespec_word = cts_int;
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:
}
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;
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))
{
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)
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
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"
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)
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"
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;
}
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;
}
}
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;
}
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;
}
#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*). */
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;
};
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 *);
{
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. */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" } */
+}