AnyVal and the @switch annotation

Should the @switch annotation be ignored in classes that extend AnyVal? If so, why?

Scala Version: 2.12.9

Compiles to bytecode using TABLESWITCH:

import scala.annotation.switch

class SwitchTest(val value: Int) {
  def test(a: Int): Int = (a: @switch) match {
    case 1 => 1
    case 2 => 2
    case _ => -1
  }
}   

Does not compile to bytecode using TABLESWITCH:

import scala.annotation.switch

class SwitchTest(val value: Int) extends AnyVal {
  def test(a: Int): Int = (a: @switch) match {
    case 1 => 1
    case 2 => 2
    case _ => -1
  }
}

It does for me.

I tested it again. This time in a new project (Intellij IDEA 2019.02.02 Ultimate, SBT 1.3.0) with nothing more than this test. Got the same result for Scala versions 2.13.1, 2.12.10, and 2.11.12 (using Java version OpenJDK 64-Bit Server VM, Java 1.8.0_222). The non-AnyVal compiles down to a TABLESWITCH operation while the AnyVal descendant compiles down to a series of conditionals.

Am I missing something??

Snippet below directly from the Scala 2.12 REPL:

$ scala
Welcome to Scala 2.12.10 (OpenJDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.

scala> :pa
// Entering paste mode (ctrl-D to finish)

import scala.annotation.switch

class SwitchTest1(val value: Int) extends AnyVal {
  def test(a: Int): Int = (a: @switch) match {
    case 1 => 1
    case 2 => 2
    case _ => -1
  }
}


class SwitchTest2(val value: Int) {
  def test(a: Int): Int = (a: @switch) match {
    case 1 => 1
    case 2 => 2
    case _ => -1
  }
}


// Exiting paste mode, now interpreting.

import scala.annotation.switch
defined class SwitchTest1
defined class SwitchTest2

scala> :javap -c SwitchTest1
Compiled from "<console>"
public final class $line3.$read$$iw$$iw$SwitchTest1 {
  public int value();
    Code:
       0: aload_0
       1: getfield      #21                 // Field value:I
       4: ireturn

  public int test(int);
    Code:
       0: getstatic     #30                 // Field $line3/$read$$iw$$iw$SwitchTest1$.MODULE$:L$line3/$read$$iw$$iw$SwitchTest1$;
       3: aload_0
       4: invokevirtual #32                 // Method value:()I
       7: iload_1
       8: invokevirtual #36                 // Method $line3/$read$$iw$$iw$SwitchTest1$.test$extension:(II)I
      11: ireturn

  public int hashCode();
    Code:
       0: getstatic     #30                 // Field $line3/$read$$iw$$iw$SwitchTest1$.MODULE$:L$line3/$read$$iw$$iw$SwitchTest1$;
       3: aload_0
       4: invokevirtual #32                 // Method value:()I
       7: invokevirtual #40                 // Method $line3/$read$$iw$$iw$SwitchTest1$.hashCode$extension:(I)I
      10: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: getstatic     #30                 // Field $line3/$read$$iw$$iw$SwitchTest1$.MODULE$:L$line3/$read$$iw$$iw$SwitchTest1$;
       3: aload_0
       4: invokevirtual #32                 // Method value:()I
       7: aload_1
       8: invokevirtual #47                 // Method $line3/$read$$iw$$iw$SwitchTest1$.equals$extension:(ILjava/lang/Object;)Z
      11: ireturn

  public $line3.$read$$iw$$iw$SwitchTest1(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #21                 // Field value:I
       5: aload_0
       6: invokespecial #53                 // Method java/lang/Object."<init>":()V
       9: return
}

scala> :javap -c SwitchTest2
Compiled from "<console>"
public class $line3.$read$$iw$$iw$SwitchTest2 {
  public int value();
    Code:
       0: aload_0
       1: getfield      #18                 // Field value:I
       4: ireturn

  public int test(int);
    Code:
       0: iload_1
       1: istore_2
       2: iload_2
       3: tableswitch   { // 1 to 2
                     1: 24
                     2: 28
               default: 32
          }
      24: iconst_1
      25: goto          36
      28: iconst_2
      29: goto          36
      32: iconst_m1
      33: goto          36
      36: ireturn

  public $line3.$read$$iw$$iw$SwitchTest2(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #18                 // Field value:I
       5: aload_0
       6: invokespecial #28                 // Method java/lang/Object."<init>":()V
       9: return
}

scala>

Where are the conditionals in bytecode? You haven’t shown them.

What I got is:

$ javap -p -c target/scala-2.12/classes/tableswitch/SwitchTest1.class 
Compiled from "TableSwitch.scala"
public final class tableswitch.SwitchTest1 {
  private final int value;

  public static boolean equals$extension(int, java.lang.Object);
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: iload_0
       4: aload_1
       5: invokevirtual #22                 // Method tableswitch/SwitchTest1$.equals$extension:(ILjava/lang/Object;)Z
       8: ireturn

  public static int hashCode$extension(int);
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: iload_0
       4: invokevirtual #26                 // Method tableswitch/SwitchTest1$.hashCode$extension:(I)I
       7: ireturn

  public static int test$extension(int, int);
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: iload_0
       4: iload_1
       5: invokevirtual #31                 // Method tableswitch/SwitchTest1$.test$extension:(II)I
       8: ireturn

  public int value();
    Code:
       0: aload_0
       1: getfield      #34                 // Field value:I
       4: ireturn

  public int test(int);
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: aload_0
       4: invokevirtual #39                 // Method value:()I
       7: iload_1
       8: invokevirtual #31                 // Method tableswitch/SwitchTest1$.test$extension:(II)I
      11: ireturn

  public int hashCode();
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: aload_0
       4: invokevirtual #39                 // Method value:()I
       7: invokevirtual #26                 // Method tableswitch/SwitchTest1$.hashCode$extension:(I)I
      10: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: getstatic     #20                 // Field tableswitch/SwitchTest1$.MODULE$:Ltableswitch/SwitchTest1$;
       3: aload_0
       4: invokevirtual #39                 // Method value:()I
       7: aload_1
       8: invokevirtual #22                 // Method tableswitch/SwitchTest1$.equals$extension:(ILjava/lang/Object;)Z
      11: ireturn

  public tableswitch.SwitchTest1(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #34                 // Field value:I
       5: aload_0
       6: invokespecial #48                 // Method java/lang/Object."<init>":()V
       9: return
}
$ javap -p -c target/scala-2.12/classes/tableswitch/SwitchTest1\$.class 
Compiled from "TableSwitch.scala"
public final class tableswitch.SwitchTest1$ {
  public static tableswitch.SwitchTest1$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class tableswitch/SwitchTest1$
       3: invokespecial #12                 // Method "<init>":()V
       6: return

  public final int test$extension(int, int);
    Code:
       0: iload_2
       1: istore_3
       2: iload_3
       3: tableswitch   { // 1 to 2
                     1: 24
                     2: 28
               default: 32
          }
      24: iconst_1
      25: goto          36
      28: iconst_2
      29: goto          36
      32: iconst_m1
      33: goto          36
      36: ireturn

  public final int hashCode$extension(int);
    Code:
       0: iload_1
       1: invokestatic  #26                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
       4: invokevirtual #30                 // Method java/lang/Object.hashCode:()I
       7: ireturn

  public final boolean equals$extension(int, java.lang.Object);
    Code:
       0: aload_2
       1: astore        4
       3: aload         4
       5: instanceof    #35                 // class tableswitch/SwitchTest1
       8: ifeq          16
      11: iconst_1
      12: istore_3
      13: goto          24
      16: goto          19
      19: iconst_0
      20: istore_3
      21: goto          24
      24: iload_3
      25: ifeq          55
      28: aload_2
      29: checkcast     #35                 // class tableswitch/SwitchTest1
      32: invokevirtual #38                 // Method tableswitch/SwitchTest1.value:()I
      35: istore        5
      37: iload_1
      38: iload         5
      40: if_icmpne     47
      43: iconst_1
      44: goto          48
      47: iconst_0
      48: ifeq          55
      51: iconst_1
      52: goto          56
      55: iconst_0
      56: ireturn

  private tableswitch.SwitchTest1$();
    Code:
       0: aload_0
       1: invokespecial #40                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #42                 // Field MODULE$:Ltableswitch/SwitchTest1$;
       8: return
}
$ javap -p -c target/scala-2.12/classes/tableswitch/SwitchTest2.class 
Compiled from "TableSwitch.scala"
public class tableswitch.SwitchTest2 {
  private final int value;

  public int value();
    Code:
       0: aload_0
       1: getfield      #13                 // Field value:I
       4: ireturn

  public int test(int);
    Code:
       0: iload_1
       1: istore_2
       2: iload_2
       3: tableswitch   { // 1 to 2
                     1: 24
                     2: 28
               default: 32
          }
      24: iconst_1
      25: goto          36
      28: iconst_2
      29: goto          36
      32: iconst_m1
      33: goto          36
      36: ireturn

  public tableswitch.SwitchTest2(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #13                 // Field value:I
       5: aload_0
       6: invokespecial #23                 // Method java/lang/Object."<init>":()V
       9: return
}

As you see tableswitch is present in both class tableswitch.SwitchTest1$ and class tableswitch.SwitchTest2.

Thank you!! I was as confused by the difference and did not catch the references to the companion object. Learned something new. I appreciate your help.