From patchwork Mon Sep 27 06:17:16 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 65809 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id B33F1B70E4 for ; Mon, 27 Sep 2010 16:17:41 +1000 (EST) Received: (qmail 23349 invoked by alias); 27 Sep 2010 06:17:38 -0000 Received: (qmail 23326 invoked by uid 22791); 27 Sep 2010 06:17:34 -0000 X-SWARE-Spam-Status: No, hits=-0.8 required=5.0 tests=AWL, BAYES_50, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, SPF_HELO_PASS, TW_MV, T_RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (74.125.121.35) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 27 Sep 2010 06:17:26 +0000 Received: from kpbe18.cbf.corp.google.com (kpbe18.cbf.corp.google.com [172.25.105.82]) by smtp-out.google.com with ESMTP id o8R6HM3s003410 for ; Sun, 26 Sep 2010 23:17:23 -0700 Received: from pvh11 (pvh11.prod.google.com [10.241.210.203]) by kpbe18.cbf.corp.google.com with ESMTP id o8R6Gxf6012495 for ; Sun, 26 Sep 2010 23:17:21 -0700 Received: by pvh11 with SMTP id 11so1357371pvh.4 for ; Sun, 26 Sep 2010 23:17:21 -0700 (PDT) Received: by 10.114.152.6 with SMTP id z6mr7841010wad.151.1285568241100; Sun, 26 Sep 2010 23:17:21 -0700 (PDT) Received: from coign.google.com (adsl-71-133-8-30.dsl.pltn13.pacbell.net [71.133.8.30]) by mx.google.com with ESMTPS id s5sm9841571wak.0.2010.09.26.23.17.18 (version=TLSv1/SSLv3 cipher=RC4-MD5); Sun, 26 Sep 2010 23:17:19 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org Subject: PATCH RFA: Support Plan 9 extensions in gcc Date: Sun, 26 Sep 2010 23:17:16 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-System-Of-Record: true X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Back in http://gcc.gnu.org/ml/gcc-patches/2009-04/msg00727.html I proposed adding an option -fplan9-extensions to gcc. This option would provide two extensions which exist in the Plan 9 C compiler. 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. At the time I could not explain what this code was for. Now I can say that it is for compiling the supporting code in the Go library. This code is written to rely on these extensions; the Go language has similar features. These parts of the Go library are shared with the other Go compiler, so maintaining a common code base is desirable. In order for gcc to compile this code, this extension is needed. There were two main objections raised last time. The first was a question about ambiguity. The Plan 9 compiler does not permit these extensions to be used when they would be ambiguous. This gcc patch acts the same way. The second was the more general objection to adding extensions to gcc. I believe that this one is reasonably limited and has an existing use case which will be part of gcc itself. Joseph Myers asked specifically about standardization; I very much doubt the Plan 9 folks care one way or another about whether this functionality is added to the standard. However, it seems to me unlikely that any contradictory functionality would be added. Bootstrapped and tested on x86_64-unknown-linux-gnu. OK for mainline? Ian gcc/ChangeLog: 2010-09-26 Ian Lance Taylor * c-typeck.c (lookup_field): If -fplan9-extensions, permit referring to a field using a typedef name. (find_anonymous_field_with_type): New static function. (convert_to_anonymous_field): New static function. (convert_for_assignment): If -fplan9-extensions, permit converting pointer to struct to pointer to anonymous field. * c-decl.c (grokfield): If -fplan9-extensions, permit anonymous fields. * doc/invoke.texi (Option Summary): Mention -fplan9-extensions. (C Dialect Options): Document -fplan9-extensions. * doc/extend.texi (Unnamed Fields): Document -fplan9-extensions. gcc/c-family/ChangeLog: 2010-09-26 Ian Lance Taylor * c.opt (-plan9-extensions): New option. gcc/testsuite/ChangeLog: 2010-09-26 Ian Lance Taylor * gcc.dg/anon-struct-11.c: New test. * gcc.dg/anon-struct-12.c: New test. * gcc.dg/anon-struct-13.c: New test. Index: doc/extend.texi =================================================================== --- doc/extend.texi (revision 164498) +++ doc/extend.texi (working copy) @@ -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 Index: doc/invoke.texi =================================================================== --- doc/invoke.texi (revision 164498) +++ doc/invoke.texi (working copy) @@ -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} Index: c-family/c.opt =================================================================== --- c-family/c.opt (revision 164498) +++ c-family/c.opt (working copy) @@ -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 Index: c-decl.c =================================================================== --- c-decl.c (revision 164498) +++ c-decl.c (working copy) @@ -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; Index: c-typeck.c =================================================================== --- c-typeck.c (revision 164498) +++ c-typeck.c (working copy) @@ -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) Index: testsuite/gcc.dg/anon-struct-11.c =================================================================== --- testsuite/gcc.dg/anon-struct-11.c (revision 0) +++ testsuite/gcc.dg/anon-struct-11.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; + }; + 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); +} Index: testsuite/gcc.dg/anon-struct-12.c =================================================================== --- testsuite/gcc.dg/anon-struct-12.c (revision 0) +++ testsuite/gcc.dg/anon-struct-12.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: testsuite/gcc.dg/anon-struct-13.c =================================================================== --- testsuite/gcc.dg/anon-struct-13.c (revision 0) +++ testsuite/gcc.dg/anon-struct-13.c (revision 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 } */