diff --git a/gcc/ChangeLog b/gcc/ChangeLog index cea88e1f31f..465b25d3e29 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2004-10-14 Joseph S. Myers + + * c-tree.h (enum c_typespec_kind, struct c_typespec, + parser_xref_tag): New. + (struct c_declspecs): Add tag_defined_p. Adjust definition of + typedef_p. + (declspecs_add_type): Adjust prototypes. + * c-parse.in (%union): Add tstype. + (typespec_nonattr, typespec_attr, typespec_reserved_nonattr, + typespec_reserved_attr, typespec_nonreserved_nonattr, + structsp_attr, structsp_nonattr): Change to tstype. Update + actions. + * c-decl.c (build_null_declspecs): Initialize tag_defined_p. + (declspecs_add_type): Update to take struct c_typespec argument. + Set tag_defined_p and typedef_p as appropriate. + (xref_tag): Rename to parser_xref_tag and replace by wrapper. + Update to return struct c_typespec. + (shadow_tag_warned): Don't let empty declarations with qualifiers + or storage class specifiers redeclare a tag if a previous + declaration is visible. + 2004-10-13 Richard Henderson PR debug/15860 diff --git a/gcc/c-decl.c b/gcc/c-decl.c index b548f850dd3..30a154cd996 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -2696,8 +2696,6 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) { bool found_tag = false; - pending_invalid_xref = 0; - if (declspecs->type && !declspecs->default_int_p && !declspecs->typedef_p) { tree value = declspecs->type; @@ -2721,8 +2719,29 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) warned = 1; } } + else if (!declspecs->tag_defined_p + && declspecs->storage_class != csc_none) + { + if (warned != 1) + pedwarn ("empty declaration with storage class specifier " + "does not redeclare tag"); + warned = 1; + pending_xref_error (); + } + else if (!declspecs->tag_defined_p + && (declspecs->const_p + || declspecs->volatile_p + || declspecs->restrict_p)) + { + if (warned != 1) + pedwarn ("empty declaration with type qualifier " + "does not redeclare tag"); + warned = 1; + pending_xref_error (); + } else { + pending_invalid_xref = 0; t = lookup_tag (code, name, 1); if (t == 0) @@ -2747,6 +2766,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) warned = 1; } + pending_invalid_xref = 0; + if (declspecs->inline_p) { error ("% in empty declaration"); @@ -4877,11 +4898,13 @@ get_parm_info (bool ellipsis) } /* Get the struct, enum or union (CODE says which) with tag NAME. - Define the tag as a forward-reference if it is not defined. */ + Define the tag as a forward-reference if it is not defined. + Return a c_typespec structure for the type specifier. */ -tree -xref_tag (enum tree_code code, tree name) +struct c_typespec +parser_xref_tag (enum tree_code code, tree name) { + struct c_typespec ret; /* If a cross reference is requested, look up the type already defined for this tag and return it. */ @@ -4897,8 +4920,12 @@ xref_tag (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); if (ref && TREE_CODE (ref) == code) - return ref; + { + ret.spec = ref; + return ret; + } /* If no such tag is yet defined, create a forward-reference node and record it as the "definition". @@ -4921,7 +4948,18 @@ xref_tag (enum tree_code code, tree name) pushtag (name, ref); - return ref; + ret.spec = ref; + return ret; +} + +/* Get the struct, enum or union (CODE says which) with tag NAME. + Define the tag as a forward-reference if it is not defined. + Return a tree for the type. */ + +tree +xref_tag (enum tree_code code, tree name) +{ + return parser_xref_tag (code, name).spec; } /* Make sure that the tag NAME is defined *in the current scope* @@ -6643,6 +6681,7 @@ build_null_declspecs (void) ret->storage_class = csc_none; ret->non_sc_seen_p = false; ret->typedef_p = false; + ret->tag_defined_p = false; ret->explicit_signed_p = false; ret->deprecated_p = false; ret->default_int_p = false; @@ -6698,8 +6737,9 @@ declspecs_add_qual (struct c_declspecs *specs, tree qual) returning SPECS. */ struct c_declspecs * -declspecs_add_type (struct c_declspecs *specs, tree type) +declspecs_add_type (struct c_declspecs *specs, struct c_typespec spec) { + tree type = spec.spec; specs->non_sc_seen_p = true; if (TREE_DEPRECATED (type)) specs->deprecated_p = true; @@ -6975,7 +7015,13 @@ declspecs_add_type (struct c_declspecs *specs, tree type) specs->type = TREE_TYPE (t); } else if (TREE_CODE (type) != ERROR_MARK) - specs->type = type; + { + if (spec.kind == ctsk_tagdef || spec.kind == ctsk_tagfirstref) + specs->tag_defined_p = true; + if (spec.kind == ctsk_typeof) + specs->typedef_p = true; + specs->type = type; + } return specs; } diff --git a/gcc/c-parse.in b/gcc/c-parse.in index e4696776c5b..7542c3959fb 100644 --- a/gcc/c-parse.in +++ b/gcc/c-parse.in @@ -101,8 +101,8 @@ do { \ %union {long itype; tree ttype; void *otype; struct c_expr exprtype; struct c_arg_info *arginfotype; struct c_declarator *dtrtype; struct c_type_name *typenametype; struct c_parm *parmtype; - struct c_declspecs *dsptype; enum tree_code code; - location_t location; } + struct c_declspecs *dsptype; struct c_typespec tstype; + enum tree_code code; location_t location; } /* All identifiers that are not reserved words and are not declared typedefs in the current block */ @@ -202,9 +202,9 @@ do { \ %type declspecs_ts_nosa declspecs_nots_nosa %type declspecs_nosc_ts declspecs_nosc_nots declspecs_nosc declspecs %type maybe_type_quals_attrs -%type typespec_nonattr typespec_attr -%type typespec_reserved_nonattr typespec_reserved_attr -%type typespec_nonreserved_nonattr +%type typespec_nonattr typespec_attr +%type typespec_reserved_nonattr typespec_reserved_attr +%type typespec_nonreserved_nonattr %type offsetof_member_designator %type scspec SCSPEC STATIC TYPESPEC TYPE_QUAL maybe_volatile @@ -226,7 +226,7 @@ do { \ %type parm_declarator_starttypename parm_declarator_nostarttypename %type array_declarator -%type structsp_attr structsp_nonattr +%type structsp_attr structsp_nonattr %type component_decl_list component_decl_list2 %type component_decl components components_notype component_declarator %type component_notype_declarator @@ -1262,7 +1262,9 @@ typespec_attr: typespec_reserved_nonattr: TYPESPEC - { OBJC_NEED_RAW_IDENTIFIER (1); } + { OBJC_NEED_RAW_IDENTIFIER (1); + $$.kind = ctsk_resword; + $$.spec = $1; } | structsp_nonattr ; @@ -1274,17 +1276,21 @@ typespec_nonreserved_nonattr: TYPENAME { /* For a typedef name, record the meaning, not the name. In case of `foo foo, bar;'. */ - $$ = lookup_name ($1); } + $$.kind = ctsk_typedef; + $$.spec = lookup_name ($1); } @@ifobjc | CLASSNAME protocolrefs - { $$ = objc_get_protocol_qualified_type ($1, $2); } + { $$.kind = ctsk_objc; + $$.spec = objc_get_protocol_qualified_type ($1, $2); } | OBJECTNAME protocolrefs - { $$ = objc_get_protocol_qualified_type ($1, $2); } + { $$.kind = ctsk_objc; + $$.spec = objc_get_protocol_qualified_type ($1, $2); } /* Make "" equivalent to "id " - nisse@lysator.liu.se */ | non_empty_protocolrefs - { $$ = objc_get_protocol_qualified_type (NULL_TREE, $1); } + { $$.kind = ctsk_objc; + $$.spec = objc_get_protocol_qualified_type (NULL_TREE, $1); } @@end_ifobjc | typeof '(' expr ')' { skip_evaluation--; @@ -1292,11 +1298,17 @@ typespec_nonreserved_nonattr: if (TREE_CODE ($3.value) == COMPONENT_REF && DECL_C_BIT_FIELD (TREE_OPERAND ($3.value, 1))) error ("% applied to a bit-field"); - $$ = TREE_TYPE ($3.value); - pop_maybe_used (variably_modified_type_p ($$, NULL_TREE)); } + $$.kind = ctsk_typeof; + $$.spec = TREE_TYPE ($3.value); + pop_maybe_used (variably_modified_type_p ($$.spec, + NULL_TREE)); } | typeof '(' typename ')' - { skip_evaluation--; in_typeof--; $$ = groktypename ($3); - pop_maybe_used (variably_modified_type_p ($$, NULL_TREE)); } + { skip_evaluation--; + in_typeof--; + $$.kind = ctsk_typeof; + $$.spec = groktypename ($3); + pop_maybe_used (variably_modified_type_p ($$.spec, + NULL_TREE)); } ; /* typespec_nonreserved_attr does not exist. */ @@ -1639,47 +1651,55 @@ enum_head: structsp_attr: struct_head identifier '{' - { $$ = start_struct (RECORD_TYPE, $2); + { $$ = start_struct (RECORD_TYPE, $2); /* Start scope of tag before parsing components. */ } component_decl_list '}' maybe_attribute - { $$ = finish_struct ($4, nreverse ($5), - chainon ($1, $7)); } + { $$.spec = finish_struct ($4, nreverse ($5), + chainon ($1, $7)); + $$.kind = ctsk_tagdef; } | struct_head '{' component_decl_list '}' maybe_attribute - { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), - nreverse ($3), chainon ($1, $5)); + { $$.spec = finish_struct (start_struct (RECORD_TYPE, + NULL_TREE), + nreverse ($3), chainon ($1, $5)); + $$.kind = ctsk_tagdef; } | union_head identifier '{' - { $$ = start_struct (UNION_TYPE, $2); } + { $$ = start_struct (UNION_TYPE, $2); } component_decl_list '}' maybe_attribute - { $$ = finish_struct ($4, nreverse ($5), - chainon ($1, $7)); } + { $$.spec = finish_struct ($4, nreverse ($5), + chainon ($1, $7)); + $$.kind = ctsk_tagdef; } | union_head '{' component_decl_list '}' maybe_attribute - { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), - nreverse ($3), chainon ($1, $5)); + { $$.spec = finish_struct (start_struct (UNION_TYPE, + NULL_TREE), + nreverse ($3), chainon ($1, $5)); + $$.kind = ctsk_tagdef; } | enum_head identifier '{' - { $$ = start_enum ($2); } + { $$ = start_enum ($2); } enumlist maybecomma_warn '}' maybe_attribute - { $$ = finish_enum ($4, nreverse ($5), - chainon ($1, $8)); } + { $$.spec = finish_enum ($4, nreverse ($5), + chainon ($1, $8)); + $$.kind = ctsk_tagdef; } | enum_head '{' - { $$ = start_enum (NULL_TREE); } + { $$ = start_enum (NULL_TREE); } enumlist maybecomma_warn '}' maybe_attribute - { $$ = finish_enum ($3, nreverse ($4), - chainon ($1, $7)); } + { $$.spec = finish_enum ($3, nreverse ($4), + chainon ($1, $7)); + $$.kind = ctsk_tagdef; } ; structsp_nonattr: struct_head identifier - { $$ = xref_tag (RECORD_TYPE, $2); } + { $$ = parser_xref_tag (RECORD_TYPE, $2); } | union_head identifier - { $$ = xref_tag (UNION_TYPE, $2); } + { $$ = parser_xref_tag (UNION_TYPE, $2); } | enum_head identifier - { $$ = xref_tag (ENUMERAL_TYPE, $2); + { $$ = parser_xref_tag (ENUMERAL_TYPE, $2); /* In ISO C, enumerated types can be referred to only if already defined. */ - if (pedantic && !COMPLETE_TYPE_P ($$)) + if (pedantic && !COMPLETE_TYPE_P ($$.spec)) pedwarn ("ISO C forbids forward references to % types"); } ; diff --git a/gcc/c-tree.h b/gcc/c-tree.h index f6e195fa101..18d200fb592 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -131,6 +131,39 @@ struct c_expr enum tree_code original_code; }; +/* A kind of type specifier. Note that this information is currently + only used to distinguish tag definitions, tag references and typeof + uses. */ +enum c_typespec_kind { + /* A reserved keyword type specifier. */ + ctsk_resword, + /* A reference to a tag, previously declared, such as "struct foo". + This includes where the previous declaration was as a different + kind of tag, in which case this is only valid if shadowing that + tag in an inner scope. */ + ctsk_tagref, + /* A reference to a tag, not previously declared in a visible + scope. */ + ctsk_tagfirstref, + /* A definition of a tag such as "struct foo { int a; }". */ + ctsk_tagdef, + /* A typedef name. */ + ctsk_typedef, + /* An ObjC-specific kind of type specifier. */ + ctsk_objc, + /* A typeof specifier. */ + ctsk_typeof +}; + +/* A type specifier: this structure is created in the parser and + passed to declspecs_add_type only. */ +struct c_typespec { + /* What kind of type specifier this is. */ + enum c_typespec_kind kind; + /* The specifier itself. */ + tree spec; +}; + /* A storage class specifier. */ enum c_storage_class { csc_none, @@ -178,8 +211,12 @@ struct c_declspecs { specifiers to be handled separately from storage class specifiers.) */ BOOL_BITFIELD non_sc_seen_p : 1; - /* Whether the type is specified by a typedef. */ + /* Whether the type is specified by a typedef or typeof name. */ BOOL_BITFIELD typedef_p : 1; + /* Whether a struct, union or enum type either had its content + defined by a type specifier in the list or was the first visible + declaration of its tag. */ + BOOL_BITFIELD tag_defined_p : 1; /* Whether the type is explicitly "signed" or specified by a typedef whose type is explicitly "signed". */ BOOL_BITFIELD explicit_signed_p : 1; @@ -371,6 +408,7 @@ extern tree start_struct (enum tree_code, tree); extern void store_parm_decls (void); extern void store_parm_decls_from (struct c_arg_info *); extern tree xref_tag (enum tree_code, tree); +extern struct c_typespec parser_xref_tag (enum tree_code, tree); extern int c_expand_decl (tree); extern struct c_parm *build_c_parm (struct c_declspecs *, tree, struct c_declarator *); @@ -383,7 +421,8 @@ extern struct c_declarator *make_pointer_declarator (struct c_declspecs *, struct c_declarator *); extern struct c_declspecs *build_null_declspecs (void); extern struct c_declspecs *declspecs_add_qual (struct c_declspecs *, tree); -extern struct c_declspecs *declspecs_add_type (struct c_declspecs *, tree); +extern struct c_declspecs *declspecs_add_type (struct c_declspecs *, + struct c_typespec); extern struct c_declspecs *declspecs_add_scspec (struct c_declspecs *, tree); extern struct c_declspecs *declspecs_add_attrs (struct c_declspecs *, tree); extern struct c_declspecs *finish_declspecs (struct c_declspecs *); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 339f7759bb7..b31a43c2f1a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2004-10-14 Joseph S. Myers + + * gcc.dg/c99-tag-3.c, gcc.dg/declspec-14.c: New tests. + 2004-10-14 Hans-Peter Nilsson PR target/17984 diff --git a/gcc/testsuite/gcc.dg/c99-tag-3.c b/gcc/testsuite/gcc.dg/c99-tag-3.c new file mode 100644 index 00000000000..12eff6e1268 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c99-tag-3.c @@ -0,0 +1,59 @@ +/* Test for handling of tags. "const struct foo;" and similar does + not redeclare an existing tag. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */ + +/* Plain "struct s;" always declares a tag: the same as one declared + in that scope, or shadowing one from an outer scope. */ +struct s0; +struct s0 { int a; }; +struct s0; +void f (void) { struct s0; } + +/* A declaration with a qualifier or storage class specifier declares + the tag if no other declaration of it is visible. */ +const union u0; /* { dg-warning "warning: useless type qualifier in empty declaration" } */ +union u0 { long b; }; + +extern struct s1; /* { dg-warning "warning: useless storage class specifier in empty declaration" } */ + +/* But if a declaration of the tag is visible, whether at the same + scope or an outer scope, the declaration specifies the same type as + the previous declaration and does not redeclare the tag (C99 + 6.7.2.3#8). Thus, as it does not declare a declarator, a tag or + the members of an enumeration, it is a constraint violation. */ + +struct s2 { char x; }; +const struct s2; /* { dg-error "error: empty declaration with type qualifier does not redeclare tag" } */ + +union u1; +extern union u1; /* { dg-error "error: empty declaration with storage class specifier does not redeclare tag" } */ + +union u2 { long b; }; +void g(void) { const union u2; } /* { dg-error "error: empty declaration with type qualifier does not redeclare tag" } */ + +/* And it does not redeclare the tag either if the outer tag is the + wrong kind of tag. This also yields an error for the reference to + the wrong kind of tag in addition to the pedwarn for the empty + declaration. */ + +union u3 { float v; }; +void h(void) { const struct u3; } /* { dg-error "'u3' defined as wrong kind of tag" } */ +/* { dg-error "error: empty declaration with type qualifier does not redeclare tag" "wrong tag empty" { target *-*-* } 42 } */ + +/* However, such useless specifiers are OK if the contents of the tag + are being defined, or shadowed in an inner scope with the contents + included in the shadowing. */ + +struct s3; +const struct s3 { int a; }; /* { dg-warning "warning: useless type qualifier in empty declaration" } */ + +union u4; +extern union u4 { int z; }; /* { dg-warning "warning: useless storage class specifier in empty declaration" } */ + +enum e0 { E0 }; +void i(void) { const enum e0 { E1 }; } /* { dg-warning "warning: useless type qualifier in empty declaration" } */ + +union u5 { int p; }; +void j(void) { extern struct u5 { int q; }; } /* { dg-warning "warning: useless storage class specifier in empty declaration" } */ diff --git a/gcc/testsuite/gcc.dg/declspec-14.c b/gcc/testsuite/gcc.dg/declspec-14.c new file mode 100644 index 00000000000..a19b9de3771 --- /dev/null +++ b/gcc/testsuite/gcc.dg/declspec-14.c @@ -0,0 +1,11 @@ +/* Test that "typeof(struct foo)" and similar as declaration + specifiers act like typedef. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "" } */ + +typeof(struct foo); /* { dg-warning "warning: useless type name in empty declaration" } */ + +struct bar { int a; } x; + +typeof(x); /* { dg-warning "warning: useless type name in empty declaration" } */