Weird Switch error in Obj-C

Solution 1:

I ran in to this problem, and one day I decided to dig in to it.

The short non-answer but pragmatic solution:

A way to work around this "problem" is to use a semi-colon, ;, right after the colon of a case ...: statement. For example, using the example you provided, it can be "fixed" so it compiles and behaves as you would intuitively expect it to:

    case 1:; // <- Note semi-colon.
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
            imagePicker.delegate = self;

The long answer:

Some history: Previously, C only allowed you to declare "block local" variables at the start of a block, which was then followed by various statements. C99 changed things so you could freely intermix variable declarations and statements.

In the context of the C99 BNF grammar, a variable declaration is a declaration, and a statement is a statement. A statement means multiple things, one of those is known as a compound-statement, which is the familiar { ... } block. The ... part is loosely defined as zero or more block-items, with a block-item being defined loosely as either a declaration or a statement.

The problem lies in the way a labeled-statement (a goto label, case label, or default:, essentially the ...: statements) is defined, which is loosely defined ...: zero or more statements. It is not, as one might intuitively expect, zero or more statements or declarations. The use of a ; right after the labeled-statements : essentially terminates the zero or more statements part of a labeled-statement. This causes the grammar to fall back to the compound-statement definition, which allows for the next "statement" to be either a statement or declaration.

I have not investigated whether or not this is an unintentional over-sight of the C99 language spec (in practical terms, a bug in the C99 standard), or if this is a pragmatic concession to the complexities of writing language grammars. If you're not familiar with writing grammars, you'll notice that the above explanation allows for recursion: A labeled-statement can match case 1: case 2: case 3:. In overly simplistic terms(1), some types of grammar recursion are simple and "unambiguous", while others are complex and "ambiguous". For simplicity, most language tools will only handle the case where any ambiguity must be deterministically resolved by looking at nothing more than "the next token". I mention this only because while this might intuitively seem like a deficiency in the C99 spec, there may be compelling, non-obvious reasons why this exists... and I haven't bothered to do any further research on the subject to find out either way.

(1) This is not meant to be a technically accurate description, but a reasonable approximation for those not familiar with the issues involved.

EDIT:

The solution I gave works in "most" cases (cases being "usages", not switch cases), but it does fail in one case: This will not work when the declaration declares a C99 variable length array, such as case 1:; void *ptrs[count]; This is because in C99 it is an error to "jump past" the declaration of a C99 VLA that is in the same lexical scope where the jump took place. In these cases, you need to use case 1: { void *ptrs[count]; }. In this case, the scope of the ptrs VLA ends at the closing }. This is more complicated than it first appears because the following is perfectly legal C code, though at first glance one would intuitively think it isn't:

switch(3){
  case 0:
    printf("case 0\n");
    break;
  case 1:;
    int *ip = NULL;
    printf("case 1\n");
    break;
  case 2:
    {
      int ia[6];
      printf("case 2\n");
      break;
      case 3:
        printf("case 3\n");
        break;
      default:
        printf("default\n");
    }
}

This compiles, and when run, prints case 3.

See also: Wikipedia: Duffs Device

Solution 2:

You can't declare variables right after a case label.

Solution 3:

One thing I like to do in this situation is to put the contents of each case within braces. This is allowed by the compiler:

switch (buttonIndex)
{
    case 0:
    {
        [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];
        break;
    }
    case 1:
    {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:[imagePicker autorelease] animated:YES];
        break;
    }
    default:
    {
        [self openEmailViewInViewController:self];
    }
}

Solution 4:

I have this kind of problem sometimes.

As weird as it may seem, after I add a NSLog before the error, it works

EDIT: In C languages you cannot instanciate objects in a switch if you don't put every "case" between { }s