From 222dbebefbbc07f78e51d82ba605988ef57e5fc9 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Sat, 1 Jan 2022 06:29:36 +0100 Subject: [PATCH] objc: Fix handling of break stmt inside of switch inside of ObjC foreach [PR103639] The r11-3302-g3696a50beeb73f changes broke the following ObjC testcase. in_statement is either 0 (not in a looping statement), various IN_* flags for various kinds of looping statements (or OpenMP structured blocks) or those flags ored with IN_SWITCH_STMT when a switch appears inside of those contexts. This is because break binds to switch in that last case, but continue binds to the looping construct in that case. The c_finish_bc_stmt function performs diagnostics on incorrect break/continue uses and then checks if in_statement & IN_OBJC_FOREACH and in that case jumps to the label provided by the caller, otherwise emits a BREAK_STMT or CONTINUE_STMT. This is incorrect if we have ObjC foreach with switch nested in it and break inside of that, in_statement in that case is IN_OBJC_FOREACH | IN_SWITCH_STMT and is_break is true. We want to handle it like other breaks inside of switch, i.e. emit a BREAK_STMT. The following patch fixes that. 2022-01-01 Jakub Jelinek PR objc/103639 * c-typeck.c (c_finish_bc_stmt): For break inside of switch inside of ObjC foreach, emit normal BREAK_STMT rather than goto to label. 2022-01-01 Iain Sandoe PR objc/103639 * objc.dg/pr103639.m: New test. --- gcc/c/c-typeck.c | 3 +- gcc/testsuite/objc.dg/pr103639.m | 101 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/objc.dg/pr103639.m diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 78a6c68..71724be 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -11257,7 +11257,8 @@ c_finish_bc_stmt (location_t loc, tree label, bool is_break) if (skip) return NULL_TREE; - else if (in_statement & IN_OBJC_FOREACH) + else if ((in_statement & IN_OBJC_FOREACH) + && !(is_break && (in_statement & IN_SWITCH_STMT))) { /* The foreach expander produces low-level code using gotos instead of a structured loop construct. */ diff --git a/gcc/testsuite/objc.dg/pr103639.m b/gcc/testsuite/objc.dg/pr103639.m new file mode 100644 index 0000000..c46e0d4 --- /dev/null +++ b/gcc/testsuite/objc.dg/pr103639.m @@ -0,0 +1,101 @@ +/* PR objc/103639 */ +/* { dg-do run } */ +/* { dg-skip-if "No NeXT fast enum. pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ +/* { dg-additional-sources "../objc-obj-c++-shared/nsconstantstring-class-impl.m" } */ +/* { dg-additional-options "-mno-constant-cfstrings" { target *-*-darwin* } } */ +/* { dg-additional-options "-Wno-objc-root-class" } */ + +#import "../objc-obj-c++-shared/TestsuiteObject.m" +#ifndef __NEXT_RUNTIME__ +#include +#else +#include "../objc-obj-c++-shared/nsconstantstring-class.h" +#endif + +extern int printf (const char *, ...); +#include + +/* 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 : TestsuiteObject +{ + 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 : TestsuiteObject +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; + return self; +} +- (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 check = 0; + +int +main() +{ + id *objects = malloc (sizeof (id) * 2); + objects[0] = @"a"; + objects[1] = @"b"; + + MyArray *array = [[MyArray alloc] initWithLength: 2 objects: objects]; + + int someVar = 0; + for (id object in array) { + switch (someVar) { + case 0: + break; + } + ++check; + } + + if (check != 2) + abort (); + return 0; +} -- 2.7.4