From patchwork Wed Oct 6 00:19:20 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicola Pero X-Patchwork-Id: 66882 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 BBEC0B6F0D for ; Wed, 6 Oct 2010 11:20:31 +1100 (EST) Received: (qmail 24281 invoked by alias); 6 Oct 2010 00:20:29 -0000 Received: (qmail 24232 invoked by uid 22791); 6 Oct 2010 00:20:07 -0000 X-SWARE-Spam-Status: No, hits=-1.1 required=5.0 tests=AWL, BAYES_00, TW_BJ, TW_TM, TW_UC, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from fencepost.gnu.org (HELO fencepost.gnu.org) (140.186.70.10) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 06 Oct 2010 00:19:45 +0000 Received: from eggs.gnu.org ([140.186.70.92]:34518) by fencepost.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1P3HjT-0005Oa-1r for gcc-patches@gnu.org; Tue, 05 Oct 2010 20:19:43 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1P3Hj7-0004CV-Oi for gcc-patches@gnu.org; Tue, 05 Oct 2010 20:19:42 -0400 Received: from smtp121.iad.emailsrvr.com ([207.97.245.121]:44476) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1P3Hj7-0004CD-H8 for gcc-patches@gnu.org; Tue, 05 Oct 2010 20:19:21 -0400 Received: from relay32.relay.iad.mlsrvr.com (localhost [127.0.0.1]) by relay32.relay.iad.mlsrvr.com (SMTP Server) with ESMTP id DE7671B4455; Tue, 5 Oct 2010 20:19:20 -0400 (EDT) Received: from dynamic10.wm-web.iad.mlsrvr.com (dynamic10.wm-web.iad.mlsrvr.com [192.168.2.217]) by relay32.relay.iad.mlsrvr.com (SMTP Server) with ESMTP id D300E1B4445; Tue, 5 Oct 2010 20:19:20 -0400 (EDT) Received: from meta-innovation.com (localhost [127.0.0.1]) by dynamic10.wm-web.iad.mlsrvr.com (Postfix) with ESMTP id AC911478807F; Tue, 5 Oct 2010 20:19:20 -0400 (EDT) Received: by www2.webmail.us (Authenticated sender: nicola.pero@meta-innovation.com, from: nicola.pero@meta-innovation.com) with HTTP; Wed, 6 Oct 2010 02:19:20 +0200 (CEST) Date: Wed, 6 Oct 2010 02:19:20 +0200 (CEST) Subject: ObjC fast enumeration, take 2 From: "Nicola Pero" To: "gcc-patches@gnu.org" Cc: joseph@codesourcery.com MIME-Version: 1.0 X-Type: plain Message-ID: <1286324360.705413345@192.168.2.228> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 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 Ok, here is a revised patch for ObjC fast enumeration. Changes are: * the "for (id object in array)" syntax is now allowed even if -std=c99 is not specified (this required changing a C function in c-decl.c, so I'm Cc:ing Joseph) * the documentation has been updated too to reflect the change * better checking for invalid fast enumeration syntax (new testcase, foreach-7.m, to test various cases, including multiple enumeration variables, missing collection expression, non-lvalues used as enumeration variables, enumeration variables being initialized in the for loop) * better error messages. Eg, instead of "selector element does not have a valid object type" (which is confusing since "selector" normally means something completely different in Objective-C) we now output "iterating variable in fast enumeration is not an object". foreach-6.m and foreach-7.m show and test various error messages, hopefully they are clear enough (I tried to use "fast enumeration" to be consistent with the documentation, and used "iterating variable" and "collection" as hopefully readable terminology for end-users) Ok to apply ? Thanks Index: gcc/doc/objc.texi =================================================================== --- gcc/doc/objc.texi (revision 165006) +++ gcc/doc/objc.texi (working copy) @@ -20,6 +20,7 @@ several resources on the Internet that present the * compatibility_alias:: * Exceptions:: * Synchronization:: +* Fast enumeration:: @end menu @node Executing code before main @@ -739,3 +740,218 @@ Because of the interactions between synchronizatio handling, you can only use @code{@@synchronized} when compiling with exceptions enabled, that is with the command line option @option{-fobjc-exceptions}. + + +@c ========================================================================= +@node Fast enumeration +@section Fast enumeration + +@menu +* Using fast enumeration:: +* c99-like fast enumeration syntax:: +* Fast enumeration details:: +* Fast enumeration protocol:: +@end menu + +@c ================================ +@node Using fast enumeration +@subsection Using fast enumeration + +GNU Objective-C provides support for the fast enumeration syntax: + +@smallexample + id array = @dots{}; + id object; + + for (object in array) + @{ + /* Do something with 'object' */ + @} +@end smallexample + +@code{array} needs to be an Objective-C object (usually a collection +object, for example an array, a dictionary or a set) which implements +the ``Fast Enumeration Protocol'' (see below). If you are using a +Foundation library such as GNUstep Base or Apple Cocoa Foundation, all +collection objects in the library implement this protocol and can be +used in this way. + +The code above would iterate over all objects in @code{array}. For +each of them, it assigns it to @code{object}, then executes the +@code{Do something with 'object'} statements. + +Here is a fully worked-out example using a Foundation library (which +provides the implementation of @code{NSArray}, @code{NSString} and +@code{NSLog}): + +@smallexample + NSArray *array = [NSArray arrayWithObjects: @@"1", @@"2", @@"3", nil]; + NSString *object; + + for (object in array) + NSLog (@@"Iterating over %@@", object); +@end smallexample + + +@c ================================ +@node c99-like fast enumeration syntax +@subsection c99-like fast enumeration syntax + +A c99-like declaration syntax is also allowed: + +@smallexample + id array = @dots{}; + + for (id object in array) + @{ + /* Do something with 'object' */ + @} +@end smallexample + +this is completely equivalent to: + +@smallexample + id array = @dots{}; + + @{ + id object; + for (object in array) + @{ + /* Do something with 'object' */ + @} + @} +@end smallexample + +but can save some typing. + +Note that the option @option{-std=c99} is not required to allow this +syntax in Objective-C. + +@c ================================ +@node Fast enumeration details +@subsection Fast enumeration details + +Here is a more technical description with the gory details. Consider the code + +@smallexample + for (@var{object expression} in @var{collection expression}) + @{ + @var{statements} + @} +@end smallexample + +here is what happens when you run it: + +@itemize @bullet +@item +@code{@var{collection expression}} is evaluated exactly once and the +result is used as the collection object to iterate over. This means +it is safe to write code such as @code{for (object in [NSDictionary +keyEnumerator]) @dots{}}. + +@item +the iteration is implemented by the compiler by repeatedly getting +batches of objects from the collection object using the fast +enumeration protocol (see below), then iterating over all objects in +the batch. This is faster than a normal enumeration where objects are +retrieved one by one (hence the name ``fast enumeration''). + +@item +if there are no objects in the collection, then +@code{@var{object expression}} is set to @code{nil} and the loop +immediately terminates. + +@item +if there are objects in the collection, then for each object in the +collection (in the order they are returned) @code{@var{object expression}} +is set to the object, then @code{@var{statements}} are executed. + +@item +@code{@var{statements}} can contain @code{break} and @code{continue} +commands, which will abort the iteration or skip to the next loop +iteration as expected. + +@item +when the iteration ends because there are no more objects to iterate +over, @code{@var{object expression}} is set to @code{nil}. This allows +you to determine whether the iteration finished because a @code{break} +command was used (in which case @code{@var{object expression}} will remain +set to the last object that was iterated over) or because it iterated +over all the objects (in which case @code{@var{object expression}} will be +set to @code{nil}). + +@item +@code{@var{statements}} must not make any changes to the collection +object; if they do, it is a hard error and the fast enumeration +terminates by invoking @code{objc_enumerationMutation}, a runtime +function that normally aborts the program but which can be customized +by Foundation libraries via @code{objc_set_mutation_handler} to do +something different, such as raising an exception. + +@end itemize + +@c ================================ +@node Fast enumeration protocol +@subsection Fast enumeration protocol + +If you want your own collection object to be usable with fast +enumeration, you need to have it implement the method + +@smallexample +- (unsigned long) countByEnumeratingWithState: (NSFastEnumerationState *)state + objects: (id *)objects + count: (unsigneld long)len; +@end smallexample + +where @code{NSFastEnumerationState} must be defined in your code as follows: + +@smallexample +typdef struct +@{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +@} NSFastEnumerationState; +@end smallexample + +If no @code{NSFastEnumerationState} is defined in your code, the +compiler will automatically replace @code{NSFastEnumerationState *} +with @code{struct __objcFastEnumerationState *}, where that type is +silently defined by the compiler in an identical way. This can be +confusing and we recommend that you define +@code{NSFastEnumerationState} (as shown above) instead. + +The method is called repeatedly during a fast enumeration to retrieve +batches of objects. Each invocation of the method should retrieve the +next batch of objects. + +The return value of the method is the number of objects in the current +batch; this should not exceed @code{len}, which is the maximum size of +a batch as requested by the caller. The batch itself is returned in +the @code{itemsPtr} field of the @code{NSFastEnumerationState} struct. + +To help with returning the objects, the @code{objects} array is a C +array preallocated by the caller (on the stack) of size @code{len}. +In many cases you can put the objects you want to return in that +@code{objects} array, then do @code{itemsPtr = objects}. But you +don't have to; if your collection already has the objects to return in +some form of C array, it could return them from there instead. + +The @code{state} and @code{extra} fields of the +@code{NSFastEnumerationState} structure allows your collection object +to keep track of the state of the enumeration. In a simple array +implementation, @code{state} may keep track of the index of the last +object that was returned, and @code{extra} may be unused. + +The @code{mutationsPtr} field of the @code{NSFastEnumerationState} is +used to keep track of mutations. It should point to a number; before +working on each object, the fast enumeration loop will check that this +number has not changed. If it has, a mutation has happened and the +fast enumeration will abort. So, @code{mutationsPtr} could be set to +point to some sort of version number of your collection, which is +increased by one every time there is a change (for example when an +object is added or removed). Or, if you are content with less strict +mutation checks, it could point to the number of objects in your +collection or some other value that can be checked to perform an +approximate check that the collection has not been mutated. Index: gcc/c-family/ChangeLog =================================================================== --- gcc/c-family/ChangeLog (revision 165006) +++ gcc/c-family/ChangeLog (working copy) @@ -1,3 +1,9 @@ +2010-10-04 Nicola Pero + + Implemented fast enumeration for Objective-C. + * c-common.h (objc_finish_foreach_loop): New. + * stub-objc.c (objc_finish_foreach_loop): New. + 2010-10-05 Joseph Myers * c-common.h (struct diagnostic_context): Don't declare here. Index: gcc/c-family/c-common.h =================================================================== --- gcc/c-family/c-common.h (revision 165006) +++ gcc/c-family/c-common.h (working copy) @@ -1007,6 +1007,7 @@ extern int objc_static_init_needed_p (void); extern tree objc_generate_static_init_call (tree); extern tree objc_generate_write_barrier (tree, enum tree_code, tree); extern void objc_set_method_opt (bool); +extern void objc_finish_foreach_loop (location_t, tree, tree, tree, tree, tree); /* The following are provided by the C and C++ front-ends, and called by ObjC/ObjC++. */ Index: gcc/c-family/stub-objc.c =================================================================== --- gcc/c-family/stub-objc.c (revision 165006) +++ gcc/c-family/stub-objc.c (working copy) @@ -355,3 +355,11 @@ objc_generate_write_barrier (tree ARG_UNUSED (lhs) { return 0; } + +void +objc_finish_foreach_loop (location_t ARG_UNUSED (location), tree ARG_UNUSED (object_expression), + tree ARG_UNUSED (collection_expression), tree ARG_UNUSED (for_body), + tree ARG_UNUSED (break_label), tree ARG_UNUSED (continue_label)) +{ + return; +} Index: gcc/objc/objc-act.c =================================================================== --- gcc/objc/objc-act.c (revision 165006) +++ gcc/objc/objc-act.c (working copy) @@ -171,6 +171,8 @@ static tree get_class_ivars (tree, bool); static tree generate_protocol_list (tree); static void build_protocol_reference (tree); +static void build_fast_enumeration_state_template (void); + #ifdef OBJCPLUS static void objc_generate_cxx_cdtors (void); #endif @@ -240,6 +242,7 @@ static void generate_struct_by_value_array (void) ATTRIBUTE_NORETURN; static void mark_referenced_methods (void); static void generate_objc_image_info (void); +static bool objc_type_valid_for_messaging (tree typ); /*** Private Interface (data) ***/ @@ -274,6 +277,9 @@ static void generate_objc_image_info (void); #define PROTOCOL_OBJECT_CLASS_NAME "Protocol" +#define TAG_ENUMERATION_MUTATION "objc_enumerationMutation" +#define TAG_FAST_ENUMERATION_STATE "__objcFastEnumerationState" + static const char *TAG_GETCLASS; static const char *TAG_GETMETACLASS; static const char *TAG_MSGSEND; @@ -1952,6 +1958,17 @@ synth_module_prologue (void) self_id = get_identifier ("self"); ucmd_id = get_identifier ("_cmd"); + /* Declare struct _objc_fast_enumeration_state { ... }; */ + build_fast_enumeration_state_template (); + + /* void objc_enumeration_mutation (id) */ + type = build_function_type (void_type_node, + tree_cons (NULL_TREE, objc_object_type, NULL_TREE)); + objc_enumeration_mutation_decl + = add_builtin_function (TAG_ENUMERATION_MUTATION, type, 0, NOT_BUILT_IN, + NULL, NULL_TREE); + TREE_NOTHROW (objc_enumeration_mutation_decl) = 0; + #ifdef OBJCPLUS pop_lang_context (); #endif @@ -3596,13 +3613,25 @@ get_class_ivars (tree interface, bool inherited) return ivar_chain; } +/* Create a temporary variable of type 'type'. If 'name' is set, uses + the specified name, else use no name. Returns the declaration of + the type. The 'name' is mostly useful for debugging. +*/ static tree -objc_create_temporary_var (tree type) +objc_create_temporary_var (tree type, const char *name) { tree decl; - decl = build_decl (input_location, - VAR_DECL, NULL_TREE, type); + if (name != NULL) + { + decl = build_decl (input_location, + VAR_DECL, get_identifier (name), type); + } + else + { + decl = build_decl (input_location, + VAR_DECL, NULL_TREE, type); + } TREE_USED (decl) = 1; DECL_ARTIFICIAL (decl) = 1; DECL_IGNORED_P (decl) = 1; @@ -3687,7 +3716,7 @@ objc_build_exc_ptr (void) tree var = cur_try_context->caught_decl; if (!var) { - var = objc_create_temporary_var (objc_object_type); + var = objc_create_temporary_var (objc_object_type, NULL); cur_try_context->caught_decl = var; } return var; @@ -3888,10 +3917,10 @@ next_sjlj_build_try_catch_finally (void) /* Create the declarations involved. */ t = xref_tag (RECORD_TYPE, get_identifier (UTAG_EXCDATA)); - stack_decl = objc_create_temporary_var (t); + stack_decl = objc_create_temporary_var (t, NULL); cur_try_context->stack_decl = stack_decl; - rethrow_decl = objc_create_temporary_var (objc_object_type); + rethrow_decl = objc_create_temporary_var (objc_object_type, NULL); cur_try_context->rethrow_decl = rethrow_decl; TREE_CHAIN (rethrow_decl) = stack_decl; @@ -9980,4 +10009,548 @@ objc_gimplify_expr (tree *expr_p, gimple_seq *pre_ #endif } +/* This routine returns true if TYP is a valid objc object type, + suitable for messaging; false otherwise. +*/ + +static bool +objc_type_valid_for_messaging (tree typ) +{ + if (!POINTER_TYPE_P (typ)) + return false; + + do + typ = TREE_TYPE (typ); /* Remove indirections. */ + while (POINTER_TYPE_P (typ)); + + if (TREE_CODE (typ) != RECORD_TYPE) + return false; + + return objc_is_object_id (typ) || TYPE_HAS_OBJC_INFO (typ); +} + +/* Begin code generation for fast enumeration (foreach) ... */ + +/* Defines + + struct __objcFastEnumerationState + { + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; + }; + + Confusingly enough, NSFastEnumeration is then defined by libraries + to be the same structure. +*/ + +static void +build_fast_enumeration_state_template (void) +{ + tree decls, *chain = NULL; + + /* { */ + objc_fast_enumeration_state_template = objc_start_struct (get_identifier + (TAG_FAST_ENUMERATION_STATE)); + + /* unsigned long state; */ + decls = add_field_decl (long_unsigned_type_node, "state", &chain); + + /* id *itemsPtr; */ + add_field_decl (build_pointer_type (objc_object_type), + "itemsPtr", &chain); + + /* unsigned long *mutationsPtr; */ + add_field_decl (build_pointer_type (long_unsigned_type_node), + "mutationsPtr", &chain); + + /* unsigned long extra[5]; */ + add_field_decl (build_sized_array_type (long_unsigned_type_node, 5), + "extra", &chain); + + /* } */ + objc_finish_struct (objc_fast_enumeration_state_template, decls); +} + +/* + 'objc_finish_foreach_loop()' generates the code for an Objective-C + foreach loop. The 'location' argument is the location of the 'for' + that starts the loop. The 'object_expression' is the expression of + the 'object' that iterates; the 'collection_expression' is the + expression of the collection that we iterate over (we need to make + sure we evaluate this only once); the 'for_body' is the set of + statements to be executed in each iteration; 'break_label' and + 'continue_label' are the break and continue labels which we need to + emit since the may be jumping to 'break_label' (if they + contain 'break') or to 'continue_label' (if they contain + 'continue'). + + The syntax is + + for ( in ) + + + which is compiled into the following blurb: + + { + id __objc_foreach_collection; + __objc_fast_enumeration_state __objc_foreach_enum_state; + unsigned long __objc_foreach_batchsize; + id __objc_foreach_items[16]; + __objc_foreach_collection = ; + __objc_foreach_enum_state = { 0 }; + __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; + + if (__objc_foreach_batchsize == 0) + = nil; + else + { + unsigned long __objc_foreach_mutations_pointer = *__objc_foreach_enum_state.mutationsPtr; + next_batch: + { + unsigned long __objc_foreach_index; + __objc_foreach_index = 0; + + next_object: + if (__objc_foreach_mutation_pointer != *__objc_foreach_enum_state.mutationsPtr) objc_enumeration_mutation (); + = enumState.itemsPtr[__objc_foreach_index]; + [PS: inside , 'break' jumps to break_label and 'continue' jumps to continue_label] + + continue_label: + __objc_foreach_index++; + if (__objc_foreach_index < __objc_foreach_batchsize) goto next_object; + __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; + } + if (__objc_foreach_batchsize != 0) goto next_batch; + = nil; + break_label: + } + } + + 'statements' may contain a 'continue' or 'break' instruction, which + the user expects to 'continue' or 'break' the entire foreach loop. + We are provided the labels that 'break' and 'continue' jump to, so + we place them where we want them to jump to when they pick them. + + Optimization TODO: we could cache the IMP of + countByEnumeratingWithState:objects:count:. +*/ + +/* If you need to debug objc_finish_foreach_loop(), uncomment the following line. */ +/* #define DEBUG_OBJC_FINISH_FOREACH_LOOP 1 */ + +#ifdef DEBUG_OBJC_FINISH_FOREACH_LOOP +#include "tree-pretty-print.h" +#endif + +void +objc_finish_foreach_loop (location_t location, tree object_expression, tree collection_expression, tree for_body, + tree break_label, tree continue_label) +{ + /* A tree representing the __objcFastEnumerationState struct type, + or NSFastEnumerationState struct, whatever we are using. */ + tree objc_fast_enumeration_state_type; + + /* The trees representing the declarations of each of the local variables. */ + tree objc_foreach_collection_decl; + tree objc_foreach_enum_state_decl; + tree objc_foreach_items_decl; + tree objc_foreach_batchsize_decl; + tree objc_foreach_mutations_pointer_decl; + tree objc_foreach_index_decl; + + /* A tree representing the selector countByEnumeratingWithState:objects:count:. */ + tree selector_name; + + /* A tree representing the local bind. */ + tree bind; + + /* A tree representing the external 'if (__objc_foreach_batchsize)' */ + tree first_if; + + /* A tree representing the 'else' part of 'first_if' */ + tree first_else; + + /* A tree representing the 'next_batch' label. */ + tree next_batch_label_decl; + + /* A tree representing the binding after the 'next_batch' label. */ + tree next_batch_bind; + + /* A tree representing the 'next_object' label. */ + tree next_object_label_decl; + + /* Temporary variables. */ + tree t; + int i; + + if (object_expression == error_mark_node) + return; + + if (collection_expression == error_mark_node) + return; + + if (!objc_type_valid_for_messaging (TREE_TYPE (object_expression))) + { + error ("iterating variable in fast enumeration is not an object"); + return; + } + + if (!objc_type_valid_for_messaging (TREE_TYPE (collection_expression))) + { + error ("collection in fast enumeration is not an object"); + return; + } + + /* TODO: Check that object_expression is either a variable + declaration, or an lvalue. */ + + /* This kludge is an idea from apple. We use the + __objcFastEnumerationState struct implicitly defined by the + compiler, unless a NSFastEnumerationState struct has been defined + (by a Foundation library such as GNUstep Base) in which case, we + use that one. + */ + objc_fast_enumeration_state_type = objc_fast_enumeration_state_template; + { + tree objc_NSFastEnumeration_type = lookup_name (get_identifier ("NSFastEnumerationState")); + + if (objc_NSFastEnumeration_type) + { + /* TODO: We really need to check that + objc_NSFastEnumeration_type is the same as ours! */ + if (TREE_CODE (objc_NSFastEnumeration_type) == TYPE_DECL) + { + /* If it's a typedef, use the original type. */ + if (DECL_ORIGINAL_TYPE (objc_NSFastEnumeration_type)) + objc_fast_enumeration_state_type = DECL_ORIGINAL_TYPE (objc_NSFastEnumeration_type); + else + objc_fast_enumeration_state_type = TREE_TYPE (objc_NSFastEnumeration_type); + } + } + } + + /* { */ + /* Done by c-parser.c. */ + + /* type object; */ + /* Done by c-parser.c. */ + + /* id __objc_foreach_collection */ + objc_foreach_collection_decl = objc_create_temporary_var (objc_object_type, "__objc_foreach_collection"); + + /* __objcFastEnumerationState __objc_foreach_enum_state; */ + objc_foreach_enum_state_decl = objc_create_temporary_var (objc_fast_enumeration_state_type, "__objc_foreach_enum_state"); + TREE_CHAIN (objc_foreach_enum_state_decl) = objc_foreach_collection_decl; + + /* id __objc_foreach_items[16]; */ + objc_foreach_items_decl = objc_create_temporary_var (build_sized_array_type (objc_object_type, 16), "__objc_foreach_items"); + TREE_CHAIN (objc_foreach_items_decl) = objc_foreach_enum_state_decl; + + /* unsigned long __objc_foreach_batchsize; */ + objc_foreach_batchsize_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_batchsize"); + TREE_CHAIN (objc_foreach_batchsize_decl) = objc_foreach_items_decl; + + /* Generate the local variable binding. */ + bind = build3 (BIND_EXPR, void_type_node, objc_foreach_batchsize_decl, NULL, NULL); + SET_EXPR_LOCATION (bind, location); + TREE_SIDE_EFFECTS (bind) = 1; + + /* __objc_foreach_collection = ; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_collection_decl, collection_expression); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + + /* __objc_foreach_enum_state.state = 0; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("state")), + build_int_cst (long_unsigned_type_node, 0)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + + /* __objc_foreach_enum_state.itemsPtr = NULL; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("itemsPtr")), + null_pointer_node); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + + /* __objc_foreach_enum_state.mutationsPtr = NULL; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("mutationsPtr")), + null_pointer_node); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + + /* __objc_foreach_enum_state.extra[0] = 0; */ + /* __objc_foreach_enum_state.extra[1] = 0; */ + /* __objc_foreach_enum_state.extra[2] = 0; */ + /* __objc_foreach_enum_state.extra[3] = 0; */ + /* __objc_foreach_enum_state.extra[4] = 0; */ + for (i = 0; i < 5 ; i++) + { + t = build2 (MODIFY_EXPR, void_type_node, + build_array_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("extra")), + build_int_cst (NULL_TREE, i)), + build_int_cst (long_unsigned_type_node, 0)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + } + + /* __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; */ + selector_name = get_identifier ("countByEnumeratingWithState:objects:count:"); +#ifdef OBJCPLUS + t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name, + /* Parameters. */ + tree_cons /* &__objc_foreach_enum_state */ + (NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl), + tree_cons /* __objc_foreach_items */ + (NULL_TREE, objc_foreach_items_decl, + tree_cons /* 16 */ + (NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE)))); +#else + /* In C, we need to decay the __objc_foreach_items array that we are passing. */ + { + struct c_expr array; + array.value = objc_foreach_items_decl; + t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name, + /* Parameters. */ + tree_cons /* &__objc_foreach_enum_state */ + (NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl), + tree_cons /* __objc_foreach_items */ + (NULL_TREE, default_function_array_conversion (location, array).value, + tree_cons /* 16 */ + (NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE)))); + } +#endif + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_batchsize_decl, t); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (bind)); + + /* if (__objc_foreach_batchsize == 0) */ + first_if = build3 (COND_EXPR, void_type_node, + /* Condition. */ + c_fully_fold + (c_common_truthvalue_conversion + (location, + build_binary_op (location, + EQ_EXPR, + objc_foreach_batchsize_decl, + build_int_cst (long_unsigned_type_node, 0), 1)), + false, NULL), + /* Then block (we fill it in later). */ + NULL_TREE, + /* Else block (we fill it in later). */ + NULL_TREE); + SET_EXPR_LOCATION (first_if, location); + append_to_statement_list (first_if, &BIND_EXPR_BODY (bind)); + + /* then = nil; */ + t = build2 (MODIFY_EXPR, void_type_node, object_expression, convert (objc_object_type, null_pointer_node)); + SET_EXPR_LOCATION (t, location); + COND_EXPR_THEN (first_if) = t; + + /* Now we build the 'else' part of the if; once we finish building + it, we attach it to first_if as the 'else' part. */ + + /* else */ + /* { */ + + /* unsigned long __objc_foreach_mutations_pointer; */ + objc_foreach_mutations_pointer_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_mutations_pointer"); + + /* Generate the local variable binding. */ + first_else = build3 (BIND_EXPR, void_type_node, objc_foreach_mutations_pointer_decl, NULL, NULL); + SET_EXPR_LOCATION (first_else, location); + TREE_SIDE_EFFECTS (first_else) = 1; + + /* __objc_foreach_mutations_pointer = *__objc_foreach_enum_state.mutationsPtr; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_mutations_pointer_decl, + build_indirect_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("mutationsPtr")), + RO_UNARY_STAR)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (first_else)); + + /* next_batch: */ + next_batch_label_decl = create_artificial_label (location); + t = build1 (LABEL_EXPR, void_type_node, next_batch_label_decl); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (first_else)); + + /* { */ + + /* unsigned long __objc_foreach_index; */ + objc_foreach_index_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_index"); + + /* Generate the local variable binding. */ + next_batch_bind = build3 (BIND_EXPR, void_type_node, objc_foreach_index_decl, NULL, NULL); + SET_EXPR_LOCATION (next_batch_bind, location); + TREE_SIDE_EFFECTS (next_batch_bind) = 1; + append_to_statement_list (next_batch_bind, &BIND_EXPR_BODY (first_else)); + + /* __objc_foreach_index = 0; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_index_decl, + build_int_cst (long_unsigned_type_node, 0)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* next_object: */ + next_object_label_decl = create_artificial_label (location); + t = build1 (LABEL_EXPR, void_type_node, next_object_label_decl); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* if (__objc_foreach_mutation_pointer != *__objc_foreach_enum_state.mutationsPtr) objc_enumeration_mutation (); */ + t = build3 (COND_EXPR, void_type_node, + /* Condition. */ + c_fully_fold + (c_common_truthvalue_conversion + (location, + build_binary_op + (location, + NE_EXPR, + objc_foreach_mutations_pointer_decl, + build_indirect_ref (location, + objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("mutationsPtr")), + RO_UNARY_STAR), 1)), + false, NULL), + /* Then block. */ + build_function_call (input_location, + objc_enumeration_mutation_decl, + tree_cons (NULL, collection_expression, NULL)), + /* Else block. */ + NULL_TREE); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* = enumState.itemsPtr[__objc_foreach_index]; */ + t = build2 (MODIFY_EXPR, void_type_node, object_expression, + build_array_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl, + get_identifier ("itemsPtr")), + objc_foreach_index_decl)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* [PS: in , 'break' jumps to break_label and 'continue' jumps to continue_label] */ + append_to_statement_list (for_body, &BIND_EXPR_BODY (next_batch_bind)); + + /* continue_label: */ + if (continue_label) + { + t = build1 (LABEL_EXPR, void_type_node, continue_label); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + } + + /* __objc_foreach_index++; */ + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_index_decl, + build_binary_op (location, + PLUS_EXPR, + objc_foreach_index_decl, + build_int_cst (long_unsigned_type_node, 1), 1)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* if (__objc_foreach_index < __objc_foreach_batchsize) goto next_object; */ + t = build3 (COND_EXPR, void_type_node, + /* Condition. */ + c_fully_fold + (c_common_truthvalue_conversion + (location, + build_binary_op (location, + LT_EXPR, + objc_foreach_index_decl, + objc_foreach_batchsize_decl, 1)), + false, NULL), + /* Then block. */ + build1 (GOTO_EXPR, void_type_node, next_object_label_decl), + /* Else block. */ + NULL_TREE); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; */ +#ifdef OBJCPLUS + t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name, + /* Parameters. */ + tree_cons /* &__objc_foreach_enum_state */ + (NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl), + tree_cons /* __objc_foreach_items */ + (NULL_TREE, objc_foreach_items_decl, + tree_cons /* 16 */ + (NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE)))); +#else + /* In C, we need to decay the __objc_foreach_items array that we are passing. */ + { + struct c_expr array; + array.value = objc_foreach_items_decl; + t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name, + /* Parameters. */ + tree_cons /* &__objc_foreach_enum_state */ + (NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl), + tree_cons /* __objc_foreach_items */ + (NULL_TREE, default_function_array_conversion (location, array).value, + tree_cons /* 16 */ + (NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE)))); + } +#endif + t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_batchsize_decl, t); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind)); + + /* } */ + + /* if (__objc_foreach_batchsize != 0) goto next_batch; */ + t = build3 (COND_EXPR, void_type_node, + /* Condition. */ + c_fully_fold + (c_common_truthvalue_conversion + (location, + build_binary_op (location, + NE_EXPR, + objc_foreach_batchsize_decl, + build_int_cst (long_unsigned_type_node, 0), 1)), + false, NULL), + /* Then block. */ + build1 (GOTO_EXPR, void_type_node, next_batch_label_decl), + /* Else block. */ + NULL_TREE); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (first_else)); + + /* = nil; */ + t = build2 (MODIFY_EXPR, void_type_node, object_expression, convert (objc_object_type, null_pointer_node)); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (first_else)); + + /* break_label: */ + if (break_label) + { + t = build1 (LABEL_EXPR, void_type_node, break_label); + SET_EXPR_LOCATION (t, location); + append_to_statement_list (t, &BIND_EXPR_BODY (first_else)); + } + + /* } */ + COND_EXPR_ELSE (first_if) = first_else; + + /* Do the whole thing. */ + add_stmt (bind); + +#ifdef DEBUG_OBJC_FINISH_FOREACH_LOOP + /* This will print to stderr the whole blurb generated by the + compiler while compiling (assuming the compiler doesn't crash + before getting here). + */ + debug_generic_stmt (bind); +#endif + + /* } */ + /* Done by c-parser.c */ +} + #include "gt-objc-objc-act.h" Index: gcc/objc/ChangeLog =================================================================== --- gcc/objc/ChangeLog (revision 165006) +++ gcc/objc/ChangeLog (working copy) @@ -1,3 +1,34 @@ +2010-10-06 Nicola Pero + + Merge from 'apple/trunk' branch on FSF servers. + + 2006-04-12 Fariborz Jahanian + + Radar 4507230 + * objc-act.c (objc_type_valid_for_messaging): New routine to check + for valid objc object types. + (objc_finish_foreach_loop): Check for invalid objc objects in + foreach header. + +2010-10-06 Nicola Pero + + Implemented fast enumeration for Objective-C. + * objc-act.c (build_fast_enumeration_state_template): New. + (TAG_ENUMERATION_MUTATION): New. + (TAG_FAST_ENUMERATION_STATE): New. + (objc_init): Call build_fast_enumeration_state_template() and set + objc_enumeration_mutation_decl, TAG_ENUMERATION_MUTATION and + TAG_FAST_ENUMERATION_STATE. + (objc_create_temporary_var): Allow providing a name to temporary + variables. + (objc_build_exc_ptr): Updated calls to + objc_create_temporary_var(). + (next_sjlj_build_try_catch_finally): Same change. + (objc_finish_foreach_loop): New. + * objc-act.h: Added OCTI_FAST_ENUM_STATE_TEMP, + OCTI_ENUM_MUTATION_DECL, objc_fast_enumeration_state_template, + objc_enumeration_mutation_decl. + 2010-10-05 Nicola Pero Merge from 'apple/trunk' branch on FSF servers. @@ -5,8 +36,8 @@ 2005-10-17 Fariborz Jahanian Radar 4290840 - * objc-act.c (objc_start_method_definition): Check for error_mark_node for - the selector name and make a quick exit. + * objc-act.c (objc_start_method_definition): Check for + error_mark_node for the selector name and make a quick exit. 2010-10-04 Andi Kleen Index: gcc/objc/objc-act.h =================================================================== --- gcc/objc/objc-act.h (revision 165006) +++ gcc/objc/objc-act.h (working copy) @@ -269,6 +269,9 @@ enum objc_tree_index OCTI_ASSIGN_GLOBAL_DECL, OCTI_ASSIGN_STRONGCAST_DECL, + OCTI_FAST_ENUM_STATE_TEMP, + OCTI_ENUM_MUTATION_DECL, + OCTI_MAX }; @@ -433,5 +436,9 @@ extern GTY(()) tree objc_global_trees[OCTI_MAX]; #define string_class_decl objc_global_trees[OCTI_STRING_CLASS_DECL] #define internal_const_str_type objc_global_trees[OCTI_INTERNAL_CNST_STR_TYPE] #define UOBJC_SUPER_decl objc_global_trees[OCTI_SUPER_DECL] +#define objc_fast_enumeration_state_template \ + objc_global_trees[OCTI_FAST_ENUM_STATE_TEMP] +#define objc_enumeration_mutation_decl \ + objc_global_trees[OCTI_ENUM_MUTATION_DECL] #endif /* GCC_OBJC_ACT_H */ Index: gcc/ChangeLog =================================================================== --- gcc/ChangeLog (revision 165006) +++ gcc/ChangeLog (working copy) @@ -1,3 +1,30 @@ +2010-10-05 Nicola Pero + + * doc/objc.texi: Document fast enumeration. + +2010-10-05 Nicola Pero + + Implemented fast enumeration for Objective-C. + * c-parser.c (objc_could_be_foreach_context): New. + (c_lex_one_token): Recognize RID_IN keyword in a potential + Objective-C foreach context. + (c_parser_declaration_or_fndef): Added parameter. Accept + Objective-C RID_IN keyword as terminating a declaration; in that + case, return the declaration in the new parameter. + (c_parser_extenral_declaration): Updated calls to + c_parser_declaration_or_fndef. + (c_parser_declaration_or_fndef): Same change. + (c_parser_compound_statement_nostart): Same change. + (c_parser_label): Same change. + (c_parser_objc_methodprotolist): Same change. + (c_parser_omp_for_loop): Same change. + (c_parser_for_statement): Detect and parse Objective-C foreach + statements. + (c_parser_omp_for_loop): Updated call to check_for_loop_decls(). + * c-decl.c (check_for_loop_decls): Added parameter to allow ObjC + fast enumeration parsing code to turn off the c99 error but still + perform checks on the loop declarations. + 2010-10-05 Anatoly Sokolov * config/m68hc11/m68hc11.h (CLASS_LIKELY_SPILLED_P): Remove. Index: gcc/testsuite/ChangeLog =================================================================== --- gcc/testsuite/ChangeLog (revision 165006) +++ gcc/testsuite/ChangeLog (working copy) @@ -1,5 +1,42 @@ 2010-10-05 Nicola Pero + Implemented fast enumeration for Objective-C. + * objc.dg/foreach-1.m: New. + * objc.dg/foreach-2.m: New. + * objc.dg/foreach-3.m: New. + * objc.dg/foreach-4.m: New. + * objc.dg/foreach-5.m: New. + * objc.dg/foreach-6.m: New. + * objc.dg/foreach-7.m: New. + + Merge from 'apple/trunk' branch on FSF servers: + 2006-04-13 Fariborz Jahanian + + Radar 4502236 + * objc.dg/objc-foreach-5.m: New. + + 2006-04-12 Fariborz Jahanian + + Radar 4507230 + * objc.dg/objc-foreach-4.m: New. + + 2006-03-13 Fariborz Jahanian + + Radar 4472881 + * objc.dg/objc-foreach-3.m: New. + + 2005-03-07 Fariborz Jahanian + + Radar 4468498 + * objc.dg/objc-foreach-2.m: New. + + 2006-02-15 Fariborz Jahanian + + Radar 4294910 + * objc.dg/objc-foreach-1.m: New + +2010-10-05 Nicola Pero + PR objc++/28050 * obj-c++.dg/syntax-error-10.mm: New. Index: gcc/testsuite/objc.dg/foreach-1.m =================================================================== --- gcc/testsuite/objc.dg/foreach-1.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-1.m (revision 0) @@ -0,0 +1,81 @@ +/* Test basic Objective-C foreach syntax. This tests iterations that + do nothing. +*/ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do run } */ + +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ +@interface Object (NSFastEnumeration) +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned int)len; +@end + +int main (void) +{ + int test_variable = 0; + int counter = 0; + id array = nil; + id object = nil; + + /* Test that 'for (object in array)' is recognized and that nothing + happens if array is nil. */ + for (object in array) + test_variable = 8; + + if (test_variable == 8) + abort (); + + if (object != nil) + abort (); + + /* Test that if nothing is done, object is set to nil. */ + object = [Object new]; + + for (object in array) + ; + + if (object != nil) + abort (); + + /* Test that you can reference 'object' inside the body. */ + for (object in array) + object = nil; + + if (object != nil) + abort (); + + /* Test that 'for (id element in array) is recognized (and works). */ + for (id element in array) + test_variable = 8; + + if (test_variable == 8) + abort (); + + /* Test that you can reference 'object' inside the body. */ + for (id element in array) + element = nil; + + /* Test that C for loops still work. */ + test_variable = 0; + + for (counter = 0; counter < 4; counter++) + test_variable++; + + if (test_variable != 4) + abort (); + + return 0; +} Index: gcc/testsuite/objc.dg/foreach-5.m =================================================================== --- gcc/testsuite/objc.dg/foreach-5.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-5.m (revision 0) @@ -0,0 +1,258 @@ +/* Test basic Objective-C foreach syntax. This tests that if you + define your own NSFastEnumeration struct, the compiler picks it up. +*/ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do run } */ + +#include +#include +#include +#include +extern void abort (void); + +typedef struct +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +} NSFastEnumerationState; + +/* A mini-array implementation that can be used to test fast + enumeration. You create the array with some objects; you can + mutate the array, and you can fast-enumerate it. +*/ +@interface MyArray : Object +{ + unsigned int length; + id *objects; + unsigned long mutated; +} +- (id) initWithLength: (unsigned int)l objects: (id *)o; +- (void) mutate; +- (unsigned long)countByEnumeratingWithState: (NSFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned long)len; +@end + +@implementation MyArray : Object +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; +} +- (void) mutate +{ + mutated = 1; +} +- (unsigned long)countByEnumeratingWithState: (NSFastEnumerationState*)state + objects: (id*)stackbuf + count: (unsigned long)len +{ + unsigned long i, batch_size; + + /* We keep how many objects we served in the state->state counter. So the next batch + will contain up to length - state->state objects. */ + batch_size = length - state->state; + + /* Make obvious adjustments. */ + if (batch_size < 0) + batch_size = 0; + + if (batch_size > len) + batch_size = len; + + /* Copy the objects. */ + for (i = 0; i < batch_size; i++) + stackbuf[i] = objects[i]; + + state->state += batch_size; + state->itemsPtr = stackbuf; + state->mutationsPtr = &mutated; + + return batch_size; +} +@end + +int main (void) +{ + MyArray *array; + int test_variable, counter, i; + id *objects; + + array = [[MyArray alloc] initWithLength: 0 + objects: NULL]; + + /* Test that an empty array does nothing. */ + for (id object in array) + abort (); + + /* Test iterating over 1 object. */ + objects = malloc (sizeof (id) * 1); + objects[0] = @"One Object"; + + array = [[MyArray alloc] initWithLength: 1 + objects: objects]; + + for (id object in array) + printf ("%p\n", object); + + /* Test iterating over 20 objects. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + + for (id object in array) + printf ("%p\n", object); + + /* Test iterating over 200 objects. */ + objects = malloc (sizeof (id) * 200); + for (i = 0; i < 200; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 200 + objects: objects]; + + counter = 0; + for (id object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test iterating again over the same array. */ + counter = 0; + for (id object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + for (id another_object in array) + if (another_object != nil) + counter++; + } + + printf ("Counter was %d (should be 400)\n", counter); + + if (counter != 400) + abort (); + + /* Test 'continue'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + if (counter == 15) + continue; + + counter++; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + counter++; + + if (counter == 15) + break; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break' and 'continue' in nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + int local_counter = 0; + + /* Each internal loop should increase counter by 24. */ + for (id another_object in array) + { + local_counter++; + + if (local_counter == 10) + { + counter = counter + 20; + break; + } + + if (local_counter >= 5) + continue; + + counter++; + } + + /* Exit after 4 iterations. */ + if (counter == 96) + break; + } + + printf ("Counter was %d (should be 96)\n", counter); + + if (counter != 96) + abort (); + + /* Test that C for loops still work. */ + test_variable = 0; + + for (counter = 0; counter < 4; counter++) + test_variable++; + + if (test_variable != 4) + abort (); + + return 0; +} Index: gcc/testsuite/objc.dg/objc-foreach-1.m =================================================================== --- gcc/testsuite/objc.dg/objc-foreach-1.m (revision 0) +++ gcc/testsuite/objc.dg/objc-foreach-1.m (revision 0) @@ -0,0 +1,41 @@ +/* Syntax check for the new foreach statement. */ +/* { dg-do compile } */ + +typedef struct objc_class *Class; + +typedef struct objc_object { + Class isa; +} *id; + + +@interface MyList +@end + +@implementation MyList +- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount +{ + return 0; +} +- (void)addObject:object { +} + +@end + +@interface MyList (BasicTest) +- (void)compilerTestAgainst; +@end +void BEGIN(); +void INFORLOOP(); +void END(); +@implementation MyList (BasicTest) +- (void)compilerTestAgainst { + + BEGIN(); + for (id elem in (self)) + if (elem) + INFORLOOP(); + + END(); +} +@end + Index: gcc/testsuite/objc.dg/objc-foreach-5.m =================================================================== --- gcc/testsuite/objc.dg/objc-foreach-5.m (revision 0) +++ gcc/testsuite/objc.dg/objc-foreach-5.m (revision 0) @@ -0,0 +1,37 @@ +/* FIXME: Run this test with the GNU runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fgnu-runtime" } { "" } } */ +/* { dg-do compile { target *-*-darwin* } } */ +#import + +NSArray * createTestVictim(unsigned capacity) { + NSMutableArray * arr = [[NSMutableArray alloc] initWithCapacity:capacity]; + int x = 0; + + for(x = 0; x < capacity; x++) { + NSNumber * num = [NSNumber numberWithInteger:x]; + [arr addObject:num]; + } + + NSArray * immutableCopy = [arr copy]; + [arr release]; + + return immutableCopy; +} + +void addStuffUp(NSArray * values) { + NSInteger accumulator = 0; +// for (id item in values) { + id item; + for (item in values) { + accumulator += [item integerValue]; + } +} + +int main (int argc, char const* argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSArray * target = createTestVictim(10); + addStuffUp(target); + [pool release]; + return 0; +} +/* { dg-final { scan-assembler "_addStuffUp:" } } */ Index: gcc/testsuite/objc.dg/foreach-2.m =================================================================== --- gcc/testsuite/objc.dg/foreach-2.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-2.m (revision 0) @@ -0,0 +1,279 @@ +/* Test basic Objective-C foreach syntax. This tests iterations, with + the basic syntax 'for (object in array) statements' +*/ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do run } */ + +#include +#include +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ + + /* A mini-array implementation that can be used to test fast + enumeration. You create the array with some objects; you can + mutate the array, and you can fast-enumerate it. + */ +@interface MyArray : Object +{ + unsigned int length; + id *objects; + unsigned long mutated; +} +- (id) initWithLength: (unsigned int)l objects: (id *)o; +- (void) mutate; +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned long)len; +@end + +@implementation MyArray : Object +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; +} +- (void) mutate +{ + mutated = 1; +} +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state + objects: (id*)stackbuf + count: (unsigned long)len +{ + unsigned long i, batch_size; + + /* We keep how many objects we served in the state->state counter. So the next batch + will contain up to length - state->state objects. */ + batch_size = length - state->state; + + /* Make obvious adjustments. */ + if (batch_size < 0) + batch_size = 0; + + if (batch_size > len) + batch_size = len; + + /* Copy the objects. */ + for (i = 0; i < batch_size; i++) + stackbuf[i] = objects[i]; + + state->state += batch_size; + state->itemsPtr = stackbuf; + state->mutationsPtr = &mutated; + + return batch_size; +} +@end + +int main (void) +{ + MyArray *array; + Object *object; + int test_variable, counter, i; + id *objects; + + array = [[MyArray alloc] initWithLength: 0 + objects: NULL]; + + /* Test that an empty array does nothing. */ + for (object in array) + abort (); + + if (object != nil) + abort (); + + /* Test iterating over 1 object. */ + objects = malloc (sizeof (id) * 1); + objects[0] = @"One Object"; + + array = [[MyArray alloc] initWithLength: 1 + objects: objects]; + + for (object in array) + printf ("%p\n", object); + + /* Test iterating over 20 objects. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + + for (object in array) + printf ("%p\n", object); + + /* Test iterating over 200 objects. */ + objects = malloc (sizeof (id) * 200); + for (i = 0; i < 200; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 200 + objects: objects]; + + counter = 0; + for (object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test iterating again over the same array. */ + counter = 0; + for (object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (object in array) + { + id another_object; + for (another_object in array) + if (another_object != nil) + counter++; + } + + printf ("Counter was %d (should be 400)\n", counter); + + if (counter != 400) + abort (); + + /* Test 'continue'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (object in array) + { + if (counter == 15) + continue; + + counter++; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (object in array) + { + counter++; + + if (counter == 15) + break; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break' and 'continue' in nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (object in array) + { + int local_counter = 0; + id another_object; + + /* Each internal loop should increase counter by 24. */ + for (another_object in array) + { + local_counter++; + + if (local_counter == 10) + { + counter = counter + 20; + break; + } + + if (local_counter >= 5) + continue; + + counter++; + } + + /* Exit after 4 iterations. */ + if (counter == 96) + break; + } + + printf ("Counter was %d (should be 96)\n", counter); + + if (counter != 96) + abort (); + + /* Test that if we 'break', the object is set to the last one, while + if we run out of objects, it is set to 'nil'. */ + for (object in array) + ; + + if (object != nil) + abort (); + + for (object in array) + break; + + if (object == nil) + abort (); + + /* Test that C for loops still work. */ + test_variable = 0; + + for (counter = 0; counter < 4; counter++) + test_variable++; + + if (test_variable != 4) + abort (); + + return 0; +} Index: gcc/testsuite/objc.dg/foreach-6.m =================================================================== --- gcc/testsuite/objc.dg/foreach-6.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-6.m (revision 0) @@ -0,0 +1,52 @@ +/* Test basic Objective-C foreach syntax. This tests warnings and errors. */ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do compile } */ + +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ +@interface Object (NSFastEnumeration) +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned int)len; +- (id) enumerator; +@end + +int main (void) +{ + id array = nil; + id object = nil; + + for (object in array) /* Ok */ + ; + + for (object in nil) /* Ok */ + ; + + for (object in) /* { dg-error "missing collection in fast enumeration" } */ + ; + + for (object = nil in array) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; + + for (object in [object enumerator]) /* Ok */ + ; + + for (12 in array) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; /* { dg-error "iterating variable in fast enumeration is not an object" } */ + + for (object in 12) + ; /* { dg-error "collection in fast enumeration is not an object" } */ + + return 0; +} Index: gcc/testsuite/objc.dg/objc-foreach-2.m =================================================================== --- gcc/testsuite/objc.dg/objc-foreach-2.m (revision 0) +++ gcc/testsuite/objc.dg/objc-foreach-2.m (revision 0) @@ -0,0 +1,41 @@ +/* Syntax check for the new foreach statement. */ +/* { dg-do compile } */ + +typedef struct objc_class *Class; + +typedef struct objc_object { + Class isa; +} *id; + + +@interface MyList +@end + +@implementation MyList +- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount +{ + return 0; +} +- (void)addObject:object { +} + +@end + +@interface MyList (BasicTest) +- (void)compilerTestAgainst; +@end +void BEGIN(); +void INFORLOOP(); +void END(); +@implementation MyList (BasicTest) +- (void)compilerTestAgainst { + + id elem; + BEGIN(); + for (elem in (self)) + if (elem) + INFORLOOP(); + END(); +} +@end + Index: gcc/testsuite/objc.dg/foreach-3.m =================================================================== --- gcc/testsuite/objc.dg/foreach-3.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-3.m (revision 0) @@ -0,0 +1,114 @@ +/* Test basic Objective-C foreach syntax. This tests the mutation. +*/ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* FIXME: This test should be run, and it succeeds if the program + aborts at the right time (when the mutation happens). It currently + works, but how do we tell the testsuite to test for it ? +*/ +/* { dg-do compile } */ + +#include +#include +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ + + /* A mini-array implementation that can be used to test fast + enumeration. You create the array with some objects; you can + mutate the array, and you can fast-enumerate it. + */ +@interface MyArray : Object +{ + unsigned int length; + id *objects; + unsigned long mutated; +} +- (id) initWithLength: (unsigned int)l objects: (id *)o; +- (void) mutate; +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned long)len; +@end + +@implementation MyArray : Object +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; +} +- (void) mutate +{ + mutated = 1; +} +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state + objects: (id*)stackbuf + count: (unsigned long)len +{ + unsigned long i, batch_size; + + /* Change the mutationsPtr if 'mutate' is called. */ + state->mutationsPtr = &mutated; + + /* We keep how many objects we served in the state->state counter. So the next batch + will contain up to length - state->state objects. */ + batch_size = length - state->state; + + /* Make obvious adjustments. */ + if (batch_size < 0) + batch_size = 0; + + if (batch_size > len) + batch_size = len; + + /* Copy the objects. */ + for (i = 0; i < batch_size; i++) + stackbuf[i] = objects[i]; + + state->state += batch_size; + state->itemsPtr = stackbuf; + + return batch_size; +} +@end + +int main (void) +{ + MyArray *array; + Object *object; + int counter, i; + id *objects; + + /* Test iterating over 20 objects, mutating after 15. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + + counter = 0; + for (object in array) + { + counter++; + printf ("%d\n", counter); + if (counter == 14) + { + printf ("Mutating (should abort at next iteration)\n"); + [array mutate]; + } + } + + return 0; +} Index: gcc/testsuite/objc.dg/foreach-7.m =================================================================== --- gcc/testsuite/objc.dg/foreach-7.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-7.m (revision 0) @@ -0,0 +1,60 @@ +/* Test basic Objective-C foreach syntax. This tests warnings and errors. */ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do compile } */ + +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ +@interface Object (NSFastEnumeration) +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned int)len; +- (id) enumerator; +@end + +void function (void) +{ + return; +} + +id object_function (void) +{ + return nil; +} + +int main (void) +{ + id array = nil; + id object = nil; + + for (typedef int my_typedef in array) /* { dg-error "declaration of non-variable" } */ + ; /* { dg-error "iterating variable in fast enumeration is not an object" } */ + + for (function () in nil) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; /* { dg-error "iterating variable in fast enumeration is not an object" } */ + + for (object_function () in nil) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; + + for ([object enumerator] in array) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; + + for (object = nil in array) /* { dg-error "invalid iterating variable in fast enumeration" } */ + ; + + for (id key, value in array) /* { dg-error "multiple iterating variables in fast enumeration" } */ + ; + + return 0; +} + Index: gcc/testsuite/objc.dg/objc-foreach-3.m =================================================================== --- gcc/testsuite/objc.dg/objc-foreach-3.m (revision 0) +++ gcc/testsuite/objc.dg/objc-foreach-3.m (revision 0) @@ -0,0 +1,42 @@ +/* Syntax check for the new foreach statement. + Use of declaration in loop-header without requiring c99 mode. */ +/* { dg-do compile } */ + +typedef struct objc_class *Class; + +typedef struct objc_object { + Class isa; +} *id; + + +@interface MyList +@end + +@implementation MyList +- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount +{ + return 0; +} +- (void)addObject:object { +} + +@end + +@interface MyList (BasicTest) +- (void)compilerTestAgainst; +@end +void BEGIN(); +void INFORLOOP(); +void END(); +@implementation MyList (BasicTest) +- (void)compilerTestAgainst { + + BEGIN(); + for (id elem in (self)) + if (elem) + INFORLOOP(); + + END(); +} +@end + Index: gcc/testsuite/objc.dg/foreach-4.m =================================================================== --- gcc/testsuite/objc.dg/foreach-4.m (revision 0) +++ gcc/testsuite/objc.dg/foreach-4.m (revision 0) @@ -0,0 +1,259 @@ +/* Test basic Objective-C foreach syntax. This tests iterations, with + the declaration syntax 'for (id object in array) statements' +*/ +/* FIXME: Run this test with the NeXT runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */ +/* { dg-do run } */ + +#include +#include +#include +#include +extern void abort (void); +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ + + /* A mini-array implementation that can be used to test fast + enumeration. You create the array with some objects; you can + mutate the array, and you can fast-enumerate it. + */ +@interface MyArray : Object +{ + unsigned int length; + id *objects; + unsigned long mutated; +} +- (id) initWithLength: (unsigned int)l objects: (id *)o; +- (void) mutate; +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned long)len; +@end + +@implementation MyArray : Object +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; +} +- (void) mutate +{ + mutated = 1; +} +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state + objects: (id*)stackbuf + count: (unsigned long)len +{ + unsigned long i, batch_size; + + /* We keep how many objects we served in the state->state counter. So the next batch + will contain up to length - state->state objects. */ + batch_size = length - state->state; + + /* Make obvious adjustments. */ + if (batch_size < 0) + batch_size = 0; + + if (batch_size > len) + batch_size = len; + + /* Copy the objects. */ + for (i = 0; i < batch_size; i++) + stackbuf[i] = objects[i]; + + state->state += batch_size; + state->itemsPtr = stackbuf; + state->mutationsPtr = &mutated; + + return batch_size; +} +@end + +int main (void) +{ + MyArray *array; + int test_variable, counter, i; + id *objects; + + array = [[MyArray alloc] initWithLength: 0 + objects: NULL]; + + /* Test that an empty array does nothing. */ + for (id object in array) + abort (); + + /* Test iterating over 1 object. */ + objects = malloc (sizeof (id) * 1); + objects[0] = @"One Object"; + + array = [[MyArray alloc] initWithLength: 1 + objects: objects]; + + for (id object in array) + printf ("%p\n", object); + + /* Test iterating over 20 objects. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + + for (id object in array) + printf ("%p\n", object); + + /* Test iterating over 200 objects. */ + objects = malloc (sizeof (id) * 200); + for (i = 0; i < 200; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 200 + objects: objects]; + + counter = 0; + for (id object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test iterating again over the same array. */ + counter = 0; + for (id object in array) + { + if (object != nil) + counter++; + } + + if (counter != 200) + abort (); + + printf ("Counter was %d (should be 200)\n", counter); + + /* Test nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + for (id another_object in array) + if (another_object != nil) + counter++; + } + + printf ("Counter was %d (should be 400)\n", counter); + + if (counter != 400) + abort (); + + /* Test 'continue'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + if (counter == 15) + continue; + + counter++; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break'. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + counter++; + + if (counter == 15) + break; + } + + printf ("Counter was %d (should be 15)\n", counter); + + if (counter != 15) + abort (); + + /* Test 'break' and 'continue' in nested iterations. */ + objects = malloc (sizeof (id) * 20); + for (i = 0; i < 20; i++) + objects[i] = @"object"; + + array = [[MyArray alloc] initWithLength: 20 + objects: objects]; + counter = 0; + for (id object in array) + { + int local_counter = 0; + + /* Each internal loop should increase counter by 24. */ + for (id another_object in array) + { + local_counter++; + + if (local_counter == 10) + { + counter = counter + 20; + break; + } + + if (local_counter >= 5) + continue; + + counter++; + } + + /* Exit after 4 iterations. */ + if (counter == 96) + break; + } + + printf ("Counter was %d (should be 96)\n", counter); + + if (counter != 96) + abort (); + + /* Test that C for loops still work. */ + test_variable = 0; + + for (counter = 0; counter < 4; counter++) + test_variable++; + + if (test_variable != 4) + abort (); + + return 0; +} Index: gcc/testsuite/objc.dg/objc-foreach-4.m =================================================================== --- gcc/testsuite/objc.dg/objc-foreach-4.m (revision 0) +++ gcc/testsuite/objc.dg/objc-foreach-4.m (revision 0) @@ -0,0 +1,17 @@ +/* Test for valid objc objects used in a for-each statement. */ +/* FIXME: Run this test with the GNU runtime as well. */ +/* { dg-skip-if "" { *-*-* } { "-fgnu-runtime" } { "" } } */ +/* { dg-do compile { target *-*-darwin* } } */ +#include + +// gcc -o foo foo.m -framework Foundation + +int main (int argc, char const* argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSArray * arr = [NSArray arrayWithObjects:@"A", @"B", @"C", nil]; + for (NSString * foo in arr) { + NSLog(@"foo is %@", foo); + } + [pool release]; + return 0; +} Index: gcc/c-tree.h =================================================================== --- gcc/c-tree.h (revision 165006) +++ gcc/c-tree.h (working copy) @@ -429,7 +429,7 @@ extern struct c_declarator *build_array_declarator bool, bool); extern tree build_enumerator (location_t, location_t, struct c_enum_contents *, tree, tree); -extern tree check_for_loop_decls (location_t); +extern tree check_for_loop_decls (location_t, bool); extern void mark_forward_parm_decls (void); extern void declare_parm_level (void); extern void undeclared_variable (location_t, tree); Index: gcc/c-decl.c =================================================================== --- gcc/c-decl.c (revision 165006) +++ gcc/c-decl.c (working copy) @@ -8310,16 +8310,23 @@ finish_function (void) /* Check the declarations given in a for-loop for satisfying the C99 constraints. If exactly one such decl is found, return it. LOC is - the location of the opening parenthesis of the for loop. */ + the location of the opening parenthesis of the for loop. The last + parameter allows you to control the "for loop initial declarations + are only allowed in C99 mode". Normally, you should pass + flag_isoc99 as that parameter. But in some cases (Objective-C + foreach loop, for example) we want to run the checks in this + function even if not in C99 mode, so we allow the caller to turn + off the error about not being in C99 mode. +*/ tree -check_for_loop_decls (location_t loc) +check_for_loop_decls (location_t loc, bool turn_off_iso_c99_error) { struct c_binding *b; tree one_decl = NULL_TREE; int n_decls = 0; - if (!flag_isoc99) + if (!turn_off_iso_c99_error) { static bool hint = true; /* If we get here, declarations have been used in a for loop without Index: gcc/c-parser.c =================================================================== --- gcc/c-parser.c (revision 165006) +++ gcc/c-parser.c (working copy) @@ -185,6 +185,11 @@ typedef struct GTY(()) c_parser { /* True if we are in a context where the Objective-C "PQ" keywords are considered keywords. */ BOOL_BITFIELD objc_pq_context : 1; + /* True if we are parsing a (potential) Objective-C foreach + statement. This is set to true after we parsed 'for (' and while + we wait for 'in' or ';' to decide if it's a standard C for loop or an + Objective-C foreach loop. */ + BOOL_BITFIELD objc_could_be_foreach_context : 1; /* The following flag is needed to contextualize Objective-C lexical analysis. In some cases (e.g., 'int NSObject;'), it is undesirable to bind an identifier to an Objective-C class, even @@ -253,6 +258,26 @@ c_lex_one_token (c_parser *parser, c_token *token) token->keyword = rid_code; break; } + else if (parser->objc_could_be_foreach_context + && rid_code == RID_IN) + { + /* We are in Objective-C, inside a (potential) + foreach context (which means after having + parsed 'for (', but before having parsed ';'), + and we found 'in'. We consider it the keyword + which terminates the declaration at the + beginning of a foreach-statement. Note that + this means you can't use 'in' for anything else + in that context; in particular, in Objective-C + you can't use 'in' as the name of the running + variable in a C for loop. We could potentially + try to add code here to disambiguate, but it + seems a reasonable limitation. + */ + token->type = CPP_KEYWORD; + token->keyword = rid_code; + break; + } /* Else, "pq" keywords outside of the "pq" context are not keywords, and we fall through to the code for normal tokens. @@ -947,7 +972,7 @@ typedef enum c_dtr_syn { static void c_parser_external_declaration (c_parser *); static void c_parser_asm_definition (c_parser *); static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, - bool, bool); + bool, bool, tree *); static void c_parser_static_assert_declaration_no_semi (c_parser *); static void c_parser_static_assert_declaration (c_parser *); static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool, @@ -1170,7 +1195,7 @@ c_parser_external_declaration (c_parser *parser) an @interface or @protocol with prefix attributes). We can only tell which after parsing the declaration specifiers, if any, and the first declarator. */ - c_parser_declaration_or_fndef (parser, true, true, true, false, true); + c_parser_declaration_or_fndef (parser, true, true, true, false, true, NULL); break; } } @@ -1189,6 +1214,8 @@ c_parser_external_declaration (c_parser *parser) (old-style parameter declarations) they are diagnosed. If START_ATTR_OK is true, the declaration specifiers may start with attributes; otherwise they may not. + OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed + declaration when parsing an Objective-C foreach statement. declaration: declaration-specifiers init-declarator-list[opt] ; @@ -1235,6 +1262,10 @@ c_parser_external_declaration (c_parser *parser) specifiers, but only at top level (elsewhere they conflict with other syntax). + In Objective-C, declarations of the looping variable in a foreach + statement are exceptionally terminated by 'in' (for example, 'for + (NSObject *object in array) { ... }'). + OpenMP: declaration: @@ -1243,7 +1274,8 @@ c_parser_external_declaration (c_parser *parser) static void c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool static_assert_ok, bool empty_ok, - bool nested, bool start_attr_ok) + bool nested, bool start_attr_ok, + tree *objc_foreach_object_declaration) { struct c_declspecs *specs; tree prefix_attrs; @@ -1375,7 +1407,8 @@ c_parser_declaration_or_fndef (c_parser *parser, b || c_parser_next_token_is (parser, CPP_COMMA) || c_parser_next_token_is (parser, CPP_SEMICOLON) || c_parser_next_token_is_keyword (parser, RID_ASM) - || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) + || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE) + || c_parser_next_token_is_keyword (parser, RID_IN)) { tree asm_name = NULL_TREE; tree postfix_attrs = NULL_TREE; @@ -1421,7 +1454,15 @@ c_parser_declaration_or_fndef (c_parser *parser, b all_prefix_attrs)); if (d) finish_decl (d, UNKNOWN_LOCATION, NULL_TREE, - NULL_TREE, asm_name); + NULL_TREE, asm_name); + + if (c_parser_next_token_is_keyword (parser, RID_IN)) + { + if (d) + *objc_foreach_object_declaration = d; + else + *objc_foreach_object_declaration = error_mark_node; + } } if (c_parser_next_token_is (parser, CPP_COMMA)) { @@ -1438,6 +1479,15 @@ c_parser_declaration_or_fndef (c_parser *parser, b c_parser_consume_token (parser); return; } + else if (c_parser_next_token_is_keyword (parser, RID_IN)) + { + /* This can only happen in Objective-C: we found the + 'in' that terminates the declaration inside an + Objective-C foreach statement. Do not consume the + token, so that the caller can use it to determine + that this indeed is a foreach context. */ + return; + } else { c_parser_error (parser, "expected %<,%> or %<;%>"); @@ -1484,7 +1534,7 @@ c_parser_declaration_or_fndef (c_parser *parser, b while (c_parser_next_token_is_not (parser, CPP_EOF) && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE)) c_parser_declaration_or_fndef (parser, false, false, false, - true, false); + true, false, NULL); store_parm_decls (); DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus = c_parser_peek_token (parser)->location; @@ -3743,7 +3793,7 @@ c_parser_compound_statement_nostart (c_parser *par { last_label = false; mark_valid_location_for_stdc_pragma (false); - c_parser_declaration_or_fndef (parser, true, true, true, true, true); + c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL); if (last_stmt) pedwarn_c90 (loc, (pedantic && !flag_isoc99) @@ -3771,7 +3821,7 @@ c_parser_compound_statement_nostart (c_parser *par last_label = false; mark_valid_location_for_stdc_pragma (false); c_parser_declaration_or_fndef (parser, true, true, true, true, - true); + true, NULL); /* Following the old parser, __extension__ does not disable this diagnostic. */ restore_extension_diagnostics (ext); @@ -3911,7 +3961,7 @@ c_parser_label (c_parser *parser) c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false, /*static_assert_ok*/ true, /*nested*/ true, /*empty_ok*/ false, - /*start_attr_ok*/ true); + /*start_attr_ok*/ true, NULL); } } } @@ -4427,20 +4477,68 @@ c_parser_do_statement (c_parser *parser) Note in particular that the nested function does not include a trailing ';', whereas the "declaration" production includes one. Also, can we reject bad declarations earlier and cheaper than - check_for_loop_decls? */ + check_for_loop_decls? + In Objective-C, there are two additional variants: + + foreach-statement: + for ( expression in expresssion ) statement + for ( declaration in expression ) statement + + This is inconsistent with C, because the second variant is allowed + even if c99 is not enabled. + + The rest of the comment documents these Objective-C foreach-statement. + + Here is the canonical example of the first variant: + for (object in array) { do something with object } + we call the first expression ("object") the "object_expression" and + the second expression ("array") the "collection_expression". + object_expression must be an lvalue of type "id" (a generic Objective-C + object) because the loop works by assigning to object_expression the + various objects from the collection_expression. collection_expression + must evaluate to something of type "id" which responds to the method + countByEnumeratingWithState:objects:count:. + + The canonical example of the second variant is: + for (id object in array) { do something with object } + which is completely equivalent to + { + id object; + for (object in array) { do something with object } + } + Note that initizializing 'object' in some way (eg, "for ((object = + xxx) in array) { do something with object }") is possibly + technically valid, but completely pointless as 'object' will be + assigned to something else as soon as the loop starts. We should + most likely reject it (TODO). + + The beginning of the Objective-C foreach-statement looks exactly + like the beginning of the for-statement, and we can tell it is a + foreach-statement only because the initial declaration or + expression is terminated by 'in' instead of ';'. +*/ + static void c_parser_for_statement (c_parser *parser) { tree block, cond, incr, save_break, save_cont, body; + /* The following are only used when parsing an ObjC foreach statement. */ + tree object_expression, collection_expression; location_t loc = c_parser_peek_token (parser)->location; location_t for_loc = c_parser_peek_token (parser)->location; + bool is_foreach_statement = false; gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR)); c_parser_consume_token (parser); - block = c_begin_compound_stmt (flag_isoc99); + /* Open a compound statement in Objective-C as well, just in case this is + as foreach expression. */ + block = c_begin_compound_stmt (flag_isoc99 || c_dialect_objc ()); if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) { /* Parse the initialization declaration or expression. */ + cond = error_mark_node; + object_expression = error_mark_node; + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { c_parser_consume_token (parser); @@ -4448,8 +4546,20 @@ c_parser_for_statement (c_parser *parser) } else if (c_parser_next_token_starts_declaration (parser)) { - c_parser_declaration_or_fndef (parser, true, true, true, true, true); - check_for_loop_decls (for_loc); + parser->objc_could_be_foreach_context = true; + c_parser_declaration_or_fndef (parser, true, true, true, true, true, + &object_expression); + parser->objc_could_be_foreach_context = false; + + if (c_parser_next_token_is_keyword (parser, RID_IN)) + { + c_parser_consume_token (parser); + is_foreach_statement = true; + if (check_for_loop_decls (for_loc, true) == NULL_TREE) + c_parser_error (parser, "multiple iterating variables in fast enumeration"); + } + else + check_for_loop_decls (for_loc, flag_isoc99); } else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) { @@ -4466,10 +4576,21 @@ c_parser_for_statement (c_parser *parser) int ext; ext = disable_extension_diagnostics (); c_parser_consume_token (parser); + parser->objc_could_be_foreach_context = true; c_parser_declaration_or_fndef (parser, true, true, true, true, - true); + true, &object_expression); + parser->objc_could_be_foreach_context = false; + restore_extension_diagnostics (ext); - check_for_loop_decls (for_loc); + if (c_parser_next_token_is_keyword (parser, RID_IN)) + { + c_parser_consume_token (parser); + is_foreach_statement = true; + if (check_for_loop_decls (for_loc, true) == NULL_TREE) + c_parser_error (parser, "multiple iterating variables in fast enumeration"); + } + else + check_for_loop_decls (for_loc, flag_isoc99); } else goto init_expr; @@ -4477,25 +4598,62 @@ c_parser_for_statement (c_parser *parser) else { init_expr: - c_finish_expr_stmt (loc, c_parser_expression (parser).value); - c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + { + tree init_expression; + parser->objc_could_be_foreach_context = true; + init_expression = c_parser_expression (parser).value; + parser->objc_could_be_foreach_context = false; + if (c_parser_next_token_is_keyword (parser, RID_IN)) + { + c_parser_consume_token (parser); + is_foreach_statement = true; + if (! lvalue_p (init_expression)) + c_parser_error (parser, "invalid iterating variable in fast enumeration"); + object_expression = c_process_expr_stmt (loc, init_expression); + + } + else + { + c_finish_expr_stmt (loc, init_expression); + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + } + } } - /* Parse the loop condition. */ - if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + /* Parse the loop condition. In the case of a foreach + statement, there is no loop condition. */ + if (!is_foreach_statement) { - c_parser_consume_token (parser); - cond = NULL_TREE; + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + { + c_parser_consume_token (parser); + cond = NULL_TREE; + } + else + { + cond = c_parser_condition (parser); + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + } } + /* Parse the increment expression (the third expression in a + for-statement). In the case of a foreach-statement, this is + the expression that follows the 'in'. */ + if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) + { + if (is_foreach_statement) + { + c_parser_error (parser, "missing collection in fast enumeration"); + collection_expression = error_mark_node; + } + else + incr = c_process_expr_stmt (loc, NULL_TREE); + } else { - cond = c_parser_condition (parser); - c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + if (is_foreach_statement) + collection_expression = c_process_expr_stmt (loc, c_parser_expression (parser).value); + else + incr = c_process_expr_stmt (loc, c_parser_expression (parser).value); } - /* Parse the increment expression. */ - if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) - incr = c_process_expr_stmt (loc, NULL_TREE); - else - incr = c_process_expr_stmt (loc, c_parser_expression (parser).value); c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); } else @@ -4508,8 +4666,11 @@ c_parser_for_statement (c_parser *parser) save_cont = c_cont_label; c_cont_label = NULL_TREE; body = c_parser_c99_block_statement (parser); - c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true); - add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); + if (is_foreach_statement) + objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label); + else + c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true); + add_stmt (c_end_compound_stmt (loc, block, flag_isoc99 || c_dialect_objc ())); c_break_label = save_break; c_cont_label = save_cont; } @@ -6790,7 +6951,7 @@ c_parser_objc_methodprotolist (c_parser *parser) } else c_parser_declaration_or_fndef (parser, false, false, true, - false, true); + false, true, NULL); break; } } @@ -8439,8 +8600,8 @@ c_parser_omp_for_loop (location_t loc, { if (i > 0) VEC_safe_push (tree, gc, for_block, c_begin_compound_stmt (true)); - c_parser_declaration_or_fndef (parser, true, true, true, true, true); - decl = check_for_loop_decls (for_loc); + c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL); + decl = check_for_loop_decls (for_loc, flag_isoc99); if (decl == NULL) goto error_init; if (DECL_INITIAL (decl) == error_mark_node) Index: libobjc/Makefile.in =================================================================== --- libobjc/Makefile.in (revision 165006) +++ libobjc/Makefile.in (working copy) @@ -120,6 +120,7 @@ OBJC_H = \ message.h \ objc-api.h \ objc-decls.h \ + runtime.h \ thr.h \ \ hash.h \ @@ -163,6 +164,7 @@ C_SOURCE_FILES = \ init.c \ memory.c \ nil_method.c \ + objc-foreach.c \ objc-sync.c \ objects.c \ sarray.c \ Index: libobjc/objc-foreach.c =================================================================== --- libobjc/objc-foreach.c (revision 0) +++ libobjc/objc-foreach.c (revision 0) @@ -0,0 +1,52 @@ +/* GNU Objective C Runtime 'fast enumeration' implementation + Copyright (C) 2010 Free Software Foundation, Inc. + Contributed by Nicola Pero + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* + This file implements objc_enumeration_mutation() and + objc_set_enumeration_mutation_handler(), the two functions required + to handle mutations during a fast enumeration. +*/ +#include "objc/runtime.h" /* For objc_enumerationMutation() and objc_set_enumeration_mutation_handler() */ +#include "objc-private/error.h" /* For _objc_abort() */ + +/* The enumeration mutation handler currently in use. */ +static void (*__objc_enumeration_mutation_handler)(id) = NULL; + +void +objc_set_enumeration_mutation_handler (void (*handler)(id)) +{ + __objc_enumeration_mutation_handler = handler; +} + +void +objc_enumerationMutation (id collection) +{ + if (__objc_enumeration_mutation_handler != NULL) + (*__objc_enumeration_mutation_handler) (collection); + + /* We always abort if we get here; there is no point in going on as + the next iteration in the fast enumeration would probably go + deeply wrong. */ + _objc_abort ("Collection %p mutated during fast enumeration", collection); +} Index: libobjc/ChangeLog =================================================================== --- libobjc/ChangeLog (revision 165006) +++ libobjc/ChangeLog (working copy) @@ -1,3 +1,11 @@ +2010-10-03 Nicola Pero + + Implemented fast enumeration for Objective-C. + * Makefile.in (C_SOURCE_FILES): Added objc-foreach.c. + (OBJC_H): Added runtime.h + * objc-foreach.c: New file. + * objc/runtime.h: New file. + 2010-09-30 Kai Tietz * objc/deprecated/struct_objc_class.h: Add padding Index: libobjc/objc/runtime.h =================================================================== --- libobjc/objc/runtime.h (revision 0) +++ libobjc/objc/runtime.h (revision 0) @@ -0,0 +1,88 @@ +/* GNU Objective-C Runtime API. + Copyright (C) 2010 Free Software Foundation, Inc. + Contributed by Nicola Pero + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#ifndef __objc_runtime_INCLUDE_GNU +#define __objc_runtime_INCLUDE_GNU + +#include "objc.h" + +/* The following is temporary, until all code from objc-api.h has been + moved into this file and objc-api.h will include runtime.h. */ +#include "objc-api.h" + +/* 'objc_enumerationMutation()' is called when a collection is + mutated while being "fast enumerated". That is a hard error, and + objc_enumerationMutation is called to deal with it. 'collection' + is the collection object that was mutated during an enumeration. + + objc_enumerationMutation() will invoke the mutation handler if any + is set. Then, it will abort the program. + + Compatibility note: the Apple runtime will not abort the program + after calling the mutation handler. + */ +objc_EXPORT void objc_enumerationMutation (id collection); + +/* 'objc_set_enumeration_mutation_handler' can be used to set a + function that will be called (instead of aborting) when a fast + enumeration is mutated during enumeration. The handler will be + called with the 'collection' being mutated as the only argument and + it should not return; it should either exit the program, or could + throw an exception. The recommended implementation is to throw an + exception - the user can then use exception handlers to deal with + it. + + This function is not thread safe (other threads may be trying to + invoke the enumeration mutation handler while you are changing it!) + and should be called during during the program initialization + before threads are started. It is mostly reserved for "Foundation" + libraries; in the case of GNUstep, GNUstep Base may be using this + function to improve the standard enumeration mutation handling. + You probably shouldn't use this function unless you are writing + your own Foundation library. +*/ +objc_EXPORT void objc_set_enumeration_mutation_handler (void (*handler)(id)); + +/* This structure (used during fast enumeration) is automatically + defined by the compiler (it is as if this definition was always + included in all Objective-C files). Note that it is usually + defined again with the name of NSFastEnumeration by "Foundation" + libraries such as GNUstep Base. And if NSFastEnumeration is + defined, the compiler will use it instead of + __objcFastEnumerationState when doing fast enumeration. +*/ +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ +/* For compatibility with the Apple/NeXT runtime. */ +#define objc_setEnumerationMutationHandler objc_set_enumeration_mutation_handler + +#endif