c-tree.h (enum c_typespec_kind, [...]): New.
authorJoseph Myers <jsm@polyomino.org.uk>
Thu, 14 Oct 2004 00:34:01 +0000 (01:34 +0100)
committerJoseph Myers <jsm28@gcc.gnu.org>
Thu, 14 Oct 2004 00:34:01 +0000 (01:34 +0100)
* 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.

testsuite:
* gcc.dg/c99-tag-3.c, gcc.dg/declspec-14.c: New tests.

From-SVN: r89021

gcc/ChangeLog
gcc/c-decl.c
gcc/c-parse.in
gcc/c-tree.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/c99-tag-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/declspec-14.c [new file with mode: 0644]

index cea88e1f31fe5ca4d098bc6096c996d3408ccf4e..465b25d3e29ace259e82e6436e2a51d49db1afa1 100644 (file)
@@ -1,3 +1,24 @@
+2004-10-14  Joseph S. Myers  <jsm@polyomino.org.uk>
+
+       * 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  <rth@redhat.com>
 
        PR debug/15860
index b548f850dd3aeb91e494a2de2cb1b49089e85edf..30a154cd9961cd1f228bcd590e5260a25d861924 100644 (file)
@@ -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 ("%<inline%> in empty declaration");
@@ -4877,11 +4898,13 @@ get_parm_info (bool ellipsis)
 }
 \f
 /* 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;
 }
 \f
 /* 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;
 }
index e4696776c5ba6c5469c98aa2c5c5eac09434afad..7542c3959fbffa306d0f001ae2597635ccdd388b 100644 (file)
@@ -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 <dsptype> declspecs_ts_nosa declspecs_nots_nosa
 %type <dsptype> declspecs_nosc_ts declspecs_nosc_nots declspecs_nosc declspecs
 %type <dsptype> maybe_type_quals_attrs
-%type <ttype> typespec_nonattr typespec_attr
-%type <ttype> typespec_reserved_nonattr typespec_reserved_attr
-%type <ttype> typespec_nonreserved_nonattr
+%type <tstype> typespec_nonattr typespec_attr
+%type <tstype> typespec_reserved_nonattr typespec_reserved_attr
+%type <tstype> typespec_nonreserved_nonattr
 %type <ttype> offsetof_member_designator
 
 %type <ttype> scspec SCSPEC STATIC TYPESPEC TYPE_QUAL maybe_volatile
@@ -226,7 +226,7 @@ do {                                                                        \
 %type <dtrtype> parm_declarator_starttypename parm_declarator_nostarttypename
 %type <dtrtype> array_declarator
 
-%type <ttype> structsp_attr structsp_nonattr
+%type <tstype> structsp_attr structsp_nonattr
 %type <ttype> component_decl_list component_decl_list2
 %type <ttype> component_decl components components_notype component_declarator
 %type <ttype> 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 "<SomeProtocol>" equivalent to "id <SomeProtocol>"
    - 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 ("%<typeof%> 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);
+               { $<ttype>$ = start_struct (RECORD_TYPE, $2);
                  /* Start scope of tag before parsing components.  */
                }
          component_decl_list '}' maybe_attribute
-               { $$ = finish_struct ($<ttype>4, nreverse ($5),
-                                     chainon ($1, $7)); }
+               { $$.spec = finish_struct ($<ttype>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); }
+               { $<ttype>$ = start_struct (UNION_TYPE, $2); }
          component_decl_list '}' maybe_attribute
-               { $$ = finish_struct ($<ttype>4, nreverse ($5),
-                                     chainon ($1, $7)); }
+               { $$.spec = finish_struct ($<ttype>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); }
+               { $<ttype>$ = start_enum ($2); }
          enumlist maybecomma_warn '}' maybe_attribute
-               { $$ = finish_enum ($<ttype>4, nreverse ($5),
-                                   chainon ($1, $8)); }
+               { $$.spec = finish_enum ($<ttype>4, nreverse ($5),
+                                        chainon ($1, $8));
+                 $$.kind = ctsk_tagdef; }
        | enum_head '{'
-               { $$ = start_enum (NULL_TREE); }
+               { $<ttype>$ = start_enum (NULL_TREE); }
          enumlist maybecomma_warn '}' maybe_attribute
-               { $$ = finish_enum ($<ttype>3, nreverse ($4),
-                                   chainon ($1, $7)); }
+               { $$.spec = finish_enum ($<ttype>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 %<enum%> types"); }
        ;
 
index f6e195fa101c673e4ef55a2070cbf263234145d0..18d200fb5921cd04e9cc62acab58614cb4c8c5ba 100644 (file)
@@ -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 *);
index 339f7759bb7aec6e2fcc46019350566d667ba99e..b31a43c2f1a5b4fd912cd69f34a0ca4bf4dee14f 100644 (file)
@@ -1,3 +1,7 @@
+2004-10-14  Joseph S. Myers  <jsm@polyomino.org.uk>
+
+       * gcc.dg/c99-tag-3.c, gcc.dg/declspec-14.c: New tests.
+
 2004-10-14  Hans-Peter Nilsson  <hp@axis.com>
 
        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 (file)
index 0000000..12eff6e
--- /dev/null
@@ -0,0 +1,59 @@
+/* Test for handling of tags.  "const struct foo;" and similar does
+   not redeclare an existing tag.  */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { 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 (file)
index 0000000..a19b9de
--- /dev/null
@@ -0,0 +1,11 @@
+/* Test that "typeof(struct foo)" and similar as declaration
+   specifiers act like typedef.  */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { 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" } */