This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Declspecs patch 6


This sixth declspecs patch, the last in the series, fixes some obscure
bugs in the handling of empty declarations in the presence of type
qualifiers, storage class specifiers or typeof.  Those with type
qualifiers and storage class specifiers are explained in the comments
in the testcase c99-tag-3.c added by the patch.  Those with typeof
arise from the logic that typeof(struct foo) should be treated like an
anonymous typedef for struct foo.  For example, whereas

struct foo;

declares the tag foo,

typeof(struct foo);

is a useless type name; the nested specifiers declare a tag, but the
declaration itself does not.

The new structure for type specifiers may also be of use in future
with more static typing: when not everything that goes in the "spec"
member is a tree, the "kind" member can be used to discriminate among
a union of different types in the "spec" member.  This patch however
keeps the changes to declspec_add_type to the minimum required for the
present patch, since such changes can be done separately: so at
present the tree still is examined to determine which path to take in
declspecs_add_type.

Bootstrapped with no regressions on i686-pc-linux-gnu.  Applied to
mainline.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

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.

testsuite:
2004-10-14  Joseph S. Myers  <jsm@polyomino.org.uk>

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

diff -rupN GCC.orig/gcc/c-decl.c GCC/gcc/c-decl.c
--- GCC.orig/gcc/c-decl.c	2004-10-10 19:17:47.000000000 +0000
+++ GCC/gcc/c-decl.c	2004-10-13 12:07:45.000000000 +0000
@@ -2696,8 +2696,6 @@ shadow_tag_warned (const struct c_declsp
 {
   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_declsp
 		  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_declsp
       warned = 1;
     }
 
+  pending_invalid_xref = 0;
+
   if (declspecs->inline_p)
     {
       error ("%<inline%> 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 *
    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->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 -rupN GCC.orig/gcc/c-parse.in GCC/gcc/c-parse.in
--- GCC.orig/gcc/c-parse.in	2004-10-10 00:44:10.000000000 +0000
+++ GCC/gcc/c-parse.in	2004-10-13 12:04:52.000000000 +0000
@@ -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"); }
 	;
 
diff -rupN GCC.orig/gcc/c-tree.h GCC/gcc/c-tree.h
--- GCC.orig/gcc/c-tree.h	2004-09-30 22:48:36.000000000 +0000
+++ GCC/gcc/c-tree.h	2004-10-13 12:04:41.000000000 +0000
@@ -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
 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
 						     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 -rupN GCC.orig/gcc/testsuite/gcc.dg/c99-tag-3.c GCC/gcc/testsuite/gcc.dg/c99-tag-3.c
--- GCC.orig/gcc/testsuite/gcc.dg/c99-tag-3.c	1970-01-01 00:00:00.000000000 +0000
+++ GCC/gcc/testsuite/gcc.dg/c99-tag-3.c	2004-10-11 12:27:23.000000000 +0000
@@ -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 -rupN GCC.orig/gcc/testsuite/gcc.dg/declspec-14.c GCC/gcc/testsuite/gcc.dg/declspec-14.c
--- GCC.orig/gcc/testsuite/gcc.dg/declspec-14.c	1970-01-01 00:00:00.000000000 +0000
+++ GCC/gcc/testsuite/gcc.dg/declspec-14.c	2004-10-11 13:38:51.000000000 +0000
@@ -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" } */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]