E115: Unable to Emit Switch

E115: Unable to Emit Switch (스위치 생성 불가)

@switch 어노테이션을 붙인 match 표현식을 JVM의 tableswitch나 lookupswitch 명령어로 컴파일할 수 없을 때 나오는 경고예요.

출처: Scala 3 Reference

본문

@switch로 어노테이션하면 컴파일러는 그 match가 tableswitch 또는 lookupswitch로 컴파일됐는지 확인하고, 만약 일련의 조건식으로 컴파일된다면 에러를 띄워요.

컴파일러는 다음의 경우 최적화를 적용하지 않아요.

  • 매치 대상 값이 Int, Byte, Short, Char 타입이 아닐 때
  • 매치 대상 값이 상수 리터럴이 아닐 때
  • case가 세 개 미만일 때

예시

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
}

에러 메시지

-- [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
    ----------------------------------------------------------------------------

해결 방법

모든 상수를 final로 만들어 인라인되게 하거나, 리터럴 값을 직접 쓰거나, 최적화가 필요 없다면 @switch를 제거하면 돼요.

// 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
}