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 <jakub@redhat.com> 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 <iain@sandoe.co.uk> PR objc/103639 * objc.dg/pr103639.m: New test.
This commit is contained in:
parent
f17d2677bc
commit
222dbebefb
2 changed files with 103 additions and 1 deletions
|
@ -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. */
|
||||
|
|
101
gcc/testsuite/objc.dg/pr103639.m
Normal file
101
gcc/testsuite/objc.dg/pr103639.m
Normal file
|
@ -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 <objc/NXConstStr.h>
|
||||
#else
|
||||
#include "../objc-obj-c++-shared/nsconstantstring-class.h"
|
||||
#endif
|
||||
|
||||
extern int printf (const char *, ...);
|
||||
#include <stdlib.h>
|
||||
|
||||
/* 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;
|
||||
}
|
Loading…
Add table
Reference in a new issue