E115: Unable to Emit Switch

This warning is emitted when the @switch annotation is used on a match expression that cannot be compiled to a JVM tableswitch or lookupswitch instruction.

If annotated with @switch, the compiler will verify that the match has been compiled to a tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional expressions.

The compiler will not apply the optimisation if:

  • the matched value is not of type Int, Byte, Short or Char
  • the matched value is not a constant literal
  • there are less than three cases

Example

import scala.annotation.switch

val ConstantB = 'B'
final val ConstantC = 'C'
def tokenMe(ch: Char) = (ch: @switch) match {
  case '\t' | '\n' => 1
  case 'A'         => 2
  case ConstantB   => 3  // a non-literal may prevent switch generation: this would not compile
  case ConstantC   => 4  // a constant value is allowed
  case _           => 5
}

Error

-- [E115] Syntax Warning: example.scala:5:38 -----------------------------------
 5 |def tokenMe(ch: Char) = (ch: @switch) match {
   |                        ^
   |                       Could not emit switch for @switch annotated match
   |
 6 |  case '\t' | '\n' => 1
 7 |...
11 |}
   |----------------------------------------------------------------------------
   | Explanation (enabled by `-explain`)
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   | If annotated with @switch, the compiler will verify that the match has been compiled to a
   | tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional
   | expressions. Example usage:
   |
   | val ConstantB = 'B'
   | final val ConstantC = 'C'
   | def tokenMe(ch: Char) = (ch: @switch) match {
   |   case '\t' | '\n' => 1
   |   case 'A'         => 2
   |   case ConstantB   => 3  // a non-literal may prevent switch generation: this would not compile
   |   case ConstantC   => 4  // a constant value is allowed
   |   case _           => 5
   | }
   |
   | The compiler will not apply the optimisation if:
   | - the matched value is not of type Int, Byte, Short or Char
   | - the matched value is not a constant literal
   | - there are less than three cases
    ----------------------------------------------------------------------------

Solution

// Make all constants final so they can be inlined
import scala.annotation.switch

final val ConstantB = 'B'
final val ConstantC = 'C'
def tokenMe(ch: Char) = (ch: @switch) match {
  case '\t' | '\n' => 1
  case 'A'         => 2
  case ConstantB   => 3
  case ConstantC   => 4
  case _           => 5
}
// Or use literal values directly
import scala.annotation.switch

def tokenMe(ch: Char) = (ch: @switch) match {
  case '\t' | '\n' => 1
  case 'A'         => 2
  case 'B'         => 3
  case 'C'         => 4
  case _           => 5
}
// Or remove @switch if optimization is not required
val ConstantB = 'B'
final val ConstantC = 'C'
def tokenMe(ch: Char) = ch match {
  case '\t' | '\n' => 1
  case 'A'         => 2
  case ConstantB   => 3
  case ConstantC   => 4
  case _           => 5
}