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]

PATCH RFA: Support Plan 9 extensions in gcc


gcc currently supports an option -fms-extensions which permits some uses
of unnamed fields in structs and unions.  The Plan 9 C compiler has two
extensions which are not included in -fms-extensions.

The first extension is if a structure has an anonymous field, a pointer
to that structure can be automatically converted to apointer to the
anonymous field in an assignment or a function call.  For example:

struct s1 { int a; };
struct s2 { int b; struct s1; };
extern void f1 (struct s1 *);
void f2 (struct s2 *p) { f1 (p); }

Here struct s2 has an anonymous field of type struct s1.  When calling
f1 from f2, the pointer to s2 is converted into a pointer to s1.  Note
that the anonymous field is not the first field in s2, so this actually
adjusts the pointer in order to make the call.

The second extension is that if a pointer has an anonymous field, and
the type of the anonymous field is a typedef for a struct or a union,
then code can refer to the entire anonymous field using the typedef
name.  For example, this is valid:

typedef struct { int a; } s3;
struct s4 { s3; };
s3 f1 (struct s4 *p) { return p->s3; }

Note the use of the typedef name s3 as the name of a field in s4.

In order to use gcc to compile code written for the Plan 9 compiler, gcc
should support these extensions.  Therefore, I propose the following
patch.  This has been bootstrapped and tested on i686-pc-linux-gnu.

OK for mainline?

If committed, I will prepare an entry for the gcc 4.5 changes.html page.

Ian


gcc/ChangeLog:

2009-04-09  Ian Lance Taylor  <iant@google.com>

	* c.opt (fplan9-extensions): New option.
	* c-typeck.c (lookup_field): If flag_plan9_extensions, permit
	using a typedef name for an anonymous field.
	(find_anonymous_field_with_type): New static function.
	(convert_to_anonymous_field): New static function.
	(convert_for_assignment): If flag_plan9_extensions, permit
	converting a pointer to a struct to a pointer to an anonymous
	field.
	* c-decl.c (grokfield): Handle flag_plan9_extensions like
	flag_ms_extensions.
	* doc/invoke.texi (Option Summary): Mention -fplan9-extensions.
	(C Dialect Options): Document -fplan9-extensions.
	* doc/extend.texi (Unnamed Fields): Document Plan 9 extensions.

gcc/testsuite/ChangeLog:

2009-04-09  Ian Lance Taylor  <iant@google.com>

	* gcc.dg/anon-struct-9.c: New test.
	* gcc.dg/anon-struct-10.c: New test.


Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 145779)
+++ doc/extend.texi	(working copy)
@@ -12094,6 +12094,32 @@ also be a definition with a tag such as 
 @samp{struct foo;}, or a reference to a @code{typedef} name for a
 previously defined structure or union type.
 
+@opindex fplan9-extensions
+The option @option{-fplan9-extensions} enables
+@option{-fms-extensions} as well as two other extensions.  First, a
+pointer to a structure is automatically converted to a pointer to an
+anonymous field for assignments and function calls.  For example:
+
+@smallexample
+struct s1 @{ int a; @};
+struct s2 @{ struct s1; @};
+extern void f1 (struct s1 *);
+void f2 (struct s2 *p) @{ f1 (p); @}
+@end smallexample
+
+In the call to @code{f1} inside @code{f2}, the pointer @code{p} is
+converted into a pointer to the anonymous field.
+
+Second, when the type of an anonymous field is a @code{typedef} for a
+@code{struct} or @code{union}, code may refer to the field using the
+name of the @code{typedef}.
+
+@smallexample
+typedef struct @{ int a; @} s1;
+struct s2 @{ s1; @};
+s1 f1 (struct s2 *p) @{ return p->s1; @}
+@end smallexample
+
 @node Thread-Local
 @section Thread-Local Storage
 @cindex Thread-Local Storage
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 145779)
+++ doc/invoke.texi	(working copy)
@@ -170,7 +170,7 @@ in the following sections.
 @gccoptlist{-ansi  -std=@var{standard}  -fgnu89-inline @gol
 -aux-info @var{filename} @gol
 -fno-asm  -fno-builtin  -fno-builtin-@var{function} @gol
--fhosted  -ffreestanding -fopenmp -fms-extensions @gol
+-fhosted  -ffreestanding -fopenmp -fms-extensions -fplan9-extensions @gol
 -trigraphs  -no-integrated-cpp  -traditional  -traditional-cpp @gol
 -fallow-single-precision  -fcond-mismatch -flax-vector-conversions @gol
 -fsigned-bitfields  -fsigned-char @gol
@@ -1591,6 +1591,16 @@ Some cases of unnamed fields in structur
 accepted with this option.  @xref{Unnamed Fields,,Unnamed struct/union
 fields within structs/unions}, for details.
 
+@item -fplan9-extensions
+Accept some non-standard constructs used in Plan 9 code.
+
+This enables @option{-fms-extensions}, and also permits passing
+pointers to structures to anonymous fields to functions which expect
+pointers to elements of the type of the field and referring to
+anonymous fields declared using a typedef.  @xref{Unnamed
+Fields,,Unnamed struct/union fields within structs/unions}, for
+details.
+
 @item -trigraphs
 @opindex trigraphs
 Support ISO C trigraphs.  The @option{-ansi} option (and @option{-std}
Index: testsuite/gcc.dg/anon-struct-9.c
===================================================================
--- testsuite/gcc.dg/anon-struct-9.c	(revision 0)
+++ testsuite/gcc.dg/anon-struct-9.c	(revision 0)
@@ -0,0 +1,111 @@
+/* { dg-do compile } */
+
+/* No special options--in particular, turn off the default
+   -pedantic-errors option.  */
+/* { dg-options "" } */
+
+/* When not using -fplan9-extensions, we don't support automatic
+   conversion of pointer types, and we don't support referring to a
+   typedef name directly.  */
+
+extern void exit (int);
+extern void abort (void);
+
+struct A { char a; };
+
+struct B {
+  char b;
+  struct A;		/* { dg-warning "does not declare anything" } */
+  char c;
+};
+
+void
+f1 (struct A *p)	/* { dg-message "expected" } */
+{
+  p->a = 1;
+}
+
+void
+test1 (void)
+{
+  struct B b;
+  struct A *p;
+
+  b.b = 2;
+  b.c = 3;
+  f1 (&b);		/* { dg-warning "incompatible pointer type" } */
+  if (b.a != 1)		/* { dg-error "no member" } */
+    abort ();
+  if (b.b != 2 || b.c != 3)
+    abort ();
+  p = &b;		/* { dg-warning "incompatible pointer type" } */
+  if (p->a != 1)
+    abort ();
+}
+
+typedef struct { char d; } D;
+
+struct E {
+  char b;
+  struct F { char f; };	/* { dg-warning "does not declare anything" } */
+  char c;
+  union {
+    D;			/* { dg-warning "does not declare anything" } */
+  };
+  char e;
+};
+
+void
+f2 (struct F *p)	/* { dg-message "expected" } */
+{
+  p->f = 6;
+}
+
+void
+f3 (D *p)		/* { dg-message "expected" } */
+{
+  p->d = 4;
+}
+
+void
+f4 (D d)
+{
+}
+
+void
+test2 (void)
+{
+  struct E e;
+  struct F *pf;
+  D *pd;
+  D d;
+
+  e.b = 2;
+  e.c = 3;
+  e.e = 5;
+  f2 (&e);		/* { dg-warning "incompatible pointer type" } */
+  f3 (&e);		/* { dg-warning "incompatible pointer type" } */
+  if (e.d != 4)		/* { dg-error "no member" } */
+    abort ();
+  if (e.f != 6)		/* { dg-error "no member" } */
+    abort ();
+  if (e.b != 2 || e.c != 3 || e.e != 5)
+    abort ();
+  pf = &e;		/* { dg-warning "incompatible pointer type" } */
+  if (pf->f != 6)
+    abort ();
+  pd = &e;		/* { dg-warning "incompatible pointer type" } */
+  if (pd->d != 4)
+    abort ();
+  d = e.D;		/* { dg-error "no member" } */
+  f3 (&e.D);		/* { dg-error "no member" } */
+  f4 (e.D);		/* { dg-error "no member" } */
+}
+
+int
+main ()
+{
+  test1 ();
+  test2 ();
+  exit (0);
+}
Index: testsuite/gcc.dg/anon-struct-10.c
===================================================================
--- testsuite/gcc.dg/anon-struct-10.c	(revision 0)
+++ testsuite/gcc.dg/anon-struct-10.c	(revision 0)
@@ -0,0 +1,108 @@
+/* { dg-do run } */
+/* { dg-options "-fplan9-extensions" } */
+
+/* When using -fplan9-extensions, we support automatic conversion of
+   pointer types, and we support referring to a typedef name
+   directly.  */
+
+extern void exit (int);
+extern void abort (void);
+
+struct A { char a; };
+
+struct B {
+  char b;
+  struct A;
+  char c;
+};
+
+void
+f1 (struct A *p)
+{
+  p->a = 1;
+}
+
+void
+test1 (void)
+{
+  struct B b;
+  struct A *p;
+
+  b.b = 2;
+  b.c = 3;
+  f1 (&b);
+  if (b.a != 1)
+    abort ();
+  if (b.b != 2 || b.c != 3)
+    abort ();
+  p = &b;
+  if (p->a != 1)
+    abort ();
+}
+
+typedef struct { char d; } D;
+
+struct E {
+  char b;
+  struct F { char f; };
+  char c;
+  union {
+    D;
+  };
+  char e;
+};
+
+void
+f2 (struct F *p)
+{
+  p->f = 6;
+}
+
+void
+f3 (D *p)
+{
+  p->d = 4;
+}
+
+void
+f4 (D d)
+{
+}
+
+void
+test2 (void)
+{
+  struct E e;
+  struct F *pf;
+  D *pd;
+  D d;
+
+  e.b = 2;
+  e.c = 3;
+  e.e = 5;
+  f2 (&e);
+  f3 (&e);
+  if (e.d != 4)
+    abort ();
+  if (e.f != 6)
+    abort ();
+  if (e.b != 2 || e.c != 3 || e.e != 5)
+    abort ();
+  pf = &e;
+  if (pf->f != 6)
+    abort ();
+  pd = &e;
+  if (pd->d != 4)
+    abort ();
+  d = e.D;
+  f3 (&e.D);
+  f4 (e.D);
+}
+
+int
+main ()
+{
+  test1 ();
+  test2 ();
+  exit (0);
+}
Index: c-decl.c
===================================================================
--- c-decl.c	(revision 145779)
+++ c-decl.c	(working copy)
@@ -5478,18 +5478,18 @@ grokfield (location_t loc,
 	 is the anonymous union extension.  Similarly for struct.
 
 	 If this is something of the form "struct foo;", then
-	   If MS extensions are enabled, this is handled as an
-	     anonymous struct.
+	   If MS or Plan 9 extensions are enabled, this is handled as
+	     an anonymous struct.
 	   Otherwise this is a forward declaration of a structure tag.
 
 	 If this is something of the form "foo;" and foo is a TYPE_DECL, then
-	   If MS extensions are enabled and foo names a structure, then
-	     again this is an anonymous struct.
+	   If MS or Plan 9 extensions are enabled and foo names a
+	     structure, then again this is an anonymous struct.
 	   Otherwise this is an error.
 
 	 Oh what a horrid tangled web we weave.  I wonder if MS consciously
-	 took this from Plan 9 or if it was an accident of implementation
-	 that took root before someone noticed the bug...  */
+	 copied Plan 9 or if it was an accident of implementation that
+	 took root before someone noticed the bug...  */
 
       tree type = declspecs->type;
       bool type_ok = (TREE_CODE (type) == RECORD_TYPE
@@ -5497,9 +5497,11 @@ grokfield (location_t loc,
       bool ok = false;
 
       if (type_ok
-	  && (flag_ms_extensions || !declspecs->typedef_p))
+	  && (flag_ms_extensions
+	      || flag_plan9_extensions
+	      || !declspecs->typedef_p))
 	{
-	  if (flag_ms_extensions)
+	  if (flag_ms_extensions || flag_plan9_extensions)
 	    ok = true;
 	  else if (flag_iso)
 	    ok = false;
Index: c.opt
===================================================================
--- c.opt	(revision 145779)
+++ c.opt	(working copy)
@@ -712,6 +712,10 @@ fpermissive
 C++ ObjC++
 Downgrade conformance errors to warnings
 
+fplan9-extensions
+C ObjC Var(flag_plan9_extensions)
+Enable Plan 9 language extensions
+
 fpreprocessed
 C ObjC C++ ObjC++
 Treat the input file as already preprocessed
Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 145779)
+++ c-typeck.c	(working copy)
@@ -1895,6 +1895,16 @@ lookup_field (tree decl, tree component)
 
 	      if (anon)
 		return tree_cons (NULL_TREE, field, anon);
+
+	      /* The Plan 9 compiler permits referring directly to an
+		 anonymous struct/union field using a typedef
+		 name.  */
+	      if (flag_plan9_extensions
+		  && TYPE_NAME (TREE_TYPE (field)) != NULL_TREE
+		  && TREE_CODE (TYPE_NAME (TREE_TYPE (field))) == TYPE_DECL
+		  && (DECL_NAME (TYPE_NAME (TREE_TYPE (field)))
+		      == component))
+		break;
 	    }
 
 	  if (DECL_NAME (field) == component)
@@ -4318,6 +4328,97 @@ build_modify_expr (location_t location,
   return result;
 }
 
+/* Return whether STRUCT_TYPE has an anonymous field with type TYPE.
+   This is used to implement -fplan9-extensions.  */
+
+static bool
+find_anonymous_field_with_type (tree struct_type, tree type)
+{
+  tree field;
+  bool found;
+
+  gcc_assert (TREE_CODE (struct_type) == RECORD_TYPE
+	      || TREE_CODE (struct_type) == UNION_TYPE);
+  found = false;
+  for (field = TYPE_FIELDS (struct_type);
+       field != NULL_TREE;
+       field = TREE_CHAIN (field))
+    {
+      if (DECL_NAME (field) == NULL
+	  && comptypes (type, TYPE_MAIN_VARIANT (TREE_TYPE (field))))
+	{
+	  if (found)
+	    return false;
+	  found = true;
+	}
+    }
+  return found;
+}
+
+/* RHS is an expression whose type is pointer to struct.  If there is
+   an anonymous field in RHS with type TYPE, then return a pointer to
+   that field in RHS.  This implements an extension to C first found
+   in the Plan 9 C compiler.  This is used with -fplan9-extensions.
+   This returns NULL if no conversion could be found.  */
+
+static tree
+convert_to_anonymous_field (tree type, tree rhs)
+{
+  tree rhs_struct_type, lhs_main_type;
+  tree field, found_field;
+  bool found_sub_field;
+  tree ret;
+
+  gcc_assert (POINTER_TYPE_P (TREE_TYPE (rhs)));
+  rhs_struct_type = TREE_TYPE (TREE_TYPE (rhs));
+  gcc_assert (TREE_CODE (rhs_struct_type) == RECORD_TYPE
+	      || TREE_CODE (rhs_struct_type) == UNION_TYPE);
+
+  gcc_assert (POINTER_TYPE_P (type));
+  lhs_main_type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+
+  found_field = NULL_TREE;
+  found_sub_field = false;
+  for (field = TYPE_FIELDS (rhs_struct_type);
+       field != NULL_TREE;
+       field = TREE_CHAIN (field))
+    {
+      if (DECL_NAME (field) != NULL_TREE
+	  || (TREE_CODE (TREE_TYPE (field)) != RECORD_TYPE
+	      && TREE_CODE (TREE_TYPE (field)) != UNION_TYPE))
+	continue;
+      if (comptypes (lhs_main_type, TYPE_MAIN_VARIANT (TREE_TYPE (field))))
+	{
+	  if (found_field != NULL_TREE)
+	    return NULL_TREE;
+	  found_field = field;
+	}
+      else if (find_anonymous_field_with_type (TREE_TYPE (field),
+					       lhs_main_type))
+	{
+	  if (found_field != NULL_TREE)
+	    return NULL_TREE;
+	  found_field = field;
+	  found_sub_field = true;
+	}
+    }
+
+  if (found_field == NULL_TREE)
+    return NULL_TREE;
+
+  ret = fold_build3 (COMPONENT_REF, TREE_TYPE (found_field),
+		     build_fold_indirect_ref (rhs), found_field, NULL_TREE);
+  ret = build_fold_addr_expr (ret);
+
+  if (found_sub_field)
+    {
+      ret = convert_to_anonymous_field (type, ret);
+      gcc_assert (ret != NULL_TREE);
+    }
+
+  return ret;
+}
+
 /* Convert value RHS to type TYPE as preparation for an assignment
    to an lvalue of type TYPE.  NULL_POINTER_CONSTANT says whether RHS
    was a null pointer constant before any folding.
@@ -4624,6 +4725,25 @@ convert_for_assignment (tree type, tree 
       /* Opaque pointers are treated like void pointers.  */
       is_opaque_pointer = vector_targets_convertible_p (ttl, ttr);
 
+      /* The Plan 9 compiler permits a pointer to a struct to be
+	 automatically converted into a pointer to an anonymous field
+	 within the struct.  */
+      if (flag_plan9_extensions
+	  && (TREE_CODE (mvl) == RECORD_TYPE || TREE_CODE(mvl) == UNION_TYPE)
+	  && (TREE_CODE (mvr) == RECORD_TYPE || TREE_CODE(mvr) == UNION_TYPE)
+	  && mvl != mvr)
+	{
+	  tree new_rhs = convert_to_anonymous_field (type, rhs);
+	  if (new_rhs != NULL_TREE)
+	    {
+	      rhs = new_rhs;
+	      rhstype = TREE_TYPE (rhs);
+	      coder = TREE_CODE (rhstype);
+	      ttr = TREE_TYPE (rhstype);
+	      mvr = TYPE_MAIN_VARIANT (ttr);
+	    }
+	}
+
       /* C++ does not allow the implicit conversion void* -> T*.  However,
 	 for the purpose of reducing the number of false positives, we
 	 tolerate the special case of

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