Switch statement

The switch statement is similar to C, except for the changes below:

* no auto-fallthrough
* auto-scoping
* default (if present) must be last

Auto-fallthrough

Unexpected fallthrough is a big source of issues in C programs, so in C2 this implicit behaviour has been removed. Any case statement must end in either: break | fallthrough | return | continue | noreturn-func. The fallthrough statement can only appear at the top-level of the case body, not in some sub-expression (eg. if (x) { fallthrough; } is not allowed).

   switch (i) {
      case 0:  // this gives an error, last statement of a case must be one of
               // break/fallthrough/continue/return.
      case 2:
         fallthrough;    // will fallthrough
      case 3:
         break;
      default:
         fallthrough; // not allowed in last case
    }

Case auto-scope

    switch (i) {
        case 1:
            i32 a = 10;     <- in C, user would have to add {} around case body.
            return calc(a);
        case 2:
            i32 a = 20;     <- no clash, since it's another scope
            ...
    }

Default last

Finally, the default statement must be last, to increase code-uniformity.

Automatic Enum scoping

C2 places enum constants in the Enum type namespace, for example:

State s = State.Begin;

For a switch statement using an enum type, the scope is automatic, so it's possible to do:

fn void demo(State s) {
    switch (s) {
    case Begin:
        break;
    case Middle:
        break;
    case End:
        break;
    }
}

So no need to add State. to every case.

Multi-condition case statements

C2 adds a new feature to C that allows multiple conditions to be specified in a single case statement. This lowers the needs for fallthrough and increases code readability. This feature can only be used when switching an enum type.

Rules for multi-condition cases:

  • Multi-conditions contain one or more single conditions, comma separated
  • A single condition can be either an identifier or identifier - identifier
  • Default cannot be combined with other conditions
  • Can also be used with incremental enums

Example:

type Foo enum u8 { A, B, C, D, E, F, G, H, I }

fn void test(Foo f) {
    switch (f) {
    case A:     // single case
        break;
    case B-C, H:   // range of enums + single
        break;
    case D-F, G, I:    // range + single cases
        break;
    }
}

Sswitch statement

C2 introduces a new statement: sswitch that can be used to make a switch-like stament, but with strings (sswitch = string switch).

fn void handleCommand(const i8* cmd) {
   sswitch (cmd) {
   case nil:
      return;
   case "start":
      return;
   case "stop": // NOTE: NO fallthrough!
   case "move":
       break;
   default:
       return 10;
   }

Rules for using sswitch:

  • empty sswitch (no cases/default) is not allowed
  • default case (if present) must be last
  • case argument must be a string literal or nil
  • there is no fallthrough (fallthrough keyword cannot be used)
  • break can be used like in switch statement