===================================================================
@@ -12985,6 +12985,34 @@ 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 with a tag.
+@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
+
+These usages are only permitted when they are not ambiguous.
+
@node Thread-Local
@section Thread-Local Storage
@cindex Thread-Local Storage
===================================================================
@@ -172,7 +172,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
@@ -1685,6 +1685,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}, permits passing pointers to
+structures with anonymous fields to functions which expect pointers to
+elements of the type of the field, and permits referring to anonymous
+fields declared using a typedef. @xref{Unnamed Fields,,Unnamed
+struct/union fields within structs/unions}, for details. This is only
+supported for C, not C++.
+
@item -trigraphs
@opindex trigraphs
Support ISO C trigraphs. The @option{-ansi} option (and @option{-std}
===================================================================
@@ -749,6 +749,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
===================================================================
@@ -6610,15 +6610,15 @@ 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 foo names a structure or union without a tag, then this
is an anonymous struct (this is permitted by C1X).
- 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
@@ -6632,7 +6632,7 @@ grokfield (location_t loc,
if (type_ok)
{
- if (flag_ms_extensions)
+ if (flag_ms_extensions || flag_plan9_extensions)
ok = true;
else if (TYPE_NAME (TYPE_MAIN_VARIANT (type)) == NULL)
ok = true;
===================================================================
@@ -2044,6 +2044,17 @@ lookup_field (tree type, 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;
}
}
@@ -2080,6 +2091,16 @@ lookup_field (tree type, 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)
@@ -4948,6 +4969,127 @@ build_modify_expr (location_t location,
return result;
}
+/* Return whether STRUCT_TYPE has an anonymous field with type TYPE.
+ If there are more than one such field, this returns NULL and sets
+ *AMBIGUOUS_P. This is used to implement -fplan9-extensions. */
+
+static bool
+find_anonymous_field_with_type (tree struct_type, tree type,
+ bool *ambiguous_p)
+{
+ 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)
+ {
+ *ambiguous_p = true;
+ return false;
+ }
+ found = true;
+ }
+ else if (DECL_NAME (field) == NULL
+ && (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
+ && find_anonymous_field_with_type (TREE_TYPE (field), type,
+ ambiguous_p))
+ {
+ if (found)
+ {
+ *ambiguous_p = true;
+ 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. If an ambiguous
+ conversion is found, this returns NULL and sets *AMBIGUOUS_P to
+ true. */
+
+static tree
+convert_to_anonymous_field (location_t location, tree type, tree rhs,
+ bool *ambiguous_p)
+{
+ 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)
+ {
+ *ambiguous_p = true;
+ return NULL_TREE;
+ }
+ found_field = field;
+ }
+ else if (find_anonymous_field_with_type (TREE_TYPE (field),
+ lhs_main_type, ambiguous_p))
+ {
+ if (found_field != NULL_TREE)
+ {
+ *ambiguous_p = true;
+ return NULL_TREE;
+ }
+ found_field = field;
+ found_sub_field = true;
+ }
+ else if (*ambiguous_p)
+ return NULL_TREE;
+ }
+
+ if (found_field == NULL_TREE)
+ return NULL_TREE;
+
+ ret = fold_build3_loc (location, COMPONENT_REF, TREE_TYPE (found_field),
+ build_fold_indirect_ref (rhs), found_field,
+ NULL_TREE);
+ ret = build_fold_addr_expr_loc (location, ret);
+
+ if (found_sub_field)
+ {
+ ret = convert_to_anonymous_field (location, type, ret, ambiguous_p);
+ gcc_assert (ret != NULL_TREE);
+ }
+
+ return ret;
+}
+
/* Convert value RHS to type TYPE as preparation for an assignment to
an lvalue of type TYPE. If ORIGTYPE is not NULL_TREE, it is the
original type of RHS; this differs from TREE_TYPE (RHS) for enum
@@ -5307,6 +5449,7 @@ convert_for_assignment (location_t locat
tree ttr = TREE_TYPE (rhstype);
tree mvl = ttl;
tree mvr = ttr;
+ bool ambiguous_p = false;
bool is_opaque_pointer;
int target_cmp = 0; /* Cache comp_target_types () result. */
addr_space_t asl;
@@ -5319,6 +5462,26 @@ convert_for_assignment (location_t locat
/* 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 (location, type, rhs,
+ &ambiguous_p);
+ 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
@@ -5492,6 +5655,9 @@ convert_for_assignment (location_t locat
"pointer type"),
G_("return from incompatible pointer type"));
+ if (ambiguous_p)
+ inform (location, "conversion to type %qT is ambiguous", type);
+
return convert (type, rhs);
}
else if (codel == POINTER_TYPE && coder == ARRAY_TYPE)
===================================================================
@@ -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;
+ };
+ 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)
+ 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);
+}
===================================================================
@@ -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);
+}
===================================================================
@@ -0,0 +1,64 @@
+/* { dg-do compile } */
+/* { dg-options "-fplan9-extensions" } */
+
+/* Test for ambiguity when using the Plan 9 extensions. */
+
+struct A {
+ char a; /* { dg-error "duplicate member" } */
+};
+
+struct B
+{
+ struct A;
+ struct A;
+};
+
+char
+f1 (struct B *p)
+{
+ return p->a; /* { dg-error "no member" } */
+}
+
+void
+f2 (struct A *p) /* { dg-message "expected" } */
+{
+}
+
+void
+f3 (struct B *p)
+{
+ f2 (p); /* { dg-warning "incompatible pointer type" } */
+}
+
+struct C
+{
+ struct A;
+ struct B;
+};
+
+char
+f4 (struct C *p)
+{
+ return p->a; /* { dg-error "no member" } */
+}
+
+void
+f5 (struct C *p)
+{
+ f2 (p); /* { dg-warning "incompatible pointer type" } */
+}
+
+struct A
+f6 (struct B *p)
+{
+ return p->A; /* { dg-error "no member" } */
+}
+
+struct A
+f7 (struct C *p)
+{
+ return p->A; /* { dg-error "no member" } */
+}
+
+/* { dg-message "ambiguous" "" { target "*-*-*" } 30 } */
+/* { dg-message "ambiguous" "" { target "*-*-*" } 48 } */