Upgrading to Scala 2.12.1 causes ClassCastExceptions

87 views
Skip to first unread message

Michael Thaler

unread,
Jan 3, 2017, 7:10:57 AM1/3/17
to scala-user
Hi,

I tried to compile / run our project with Scala 2.12.1 and now get ClassCastExceptions. The code ran fine with Scala 2.11.8.

The following code shows the problem:

package variant

object Variant {
sealed trait ValueType
case object ByteValue extends ValueType
case object ShortValue extends ValueType
case object IntValue extends ValueType
case object LongValue extends ValueType


trait Unwrapper[@specialized(Byte, Short, Int, Long) P] {
def unwrap(v: Variant): P = v.value.asInstanceOf[P]
}

def apply(value: Byte): Variant = Variant(value, ByteValue)

def apply(value: Short): Variant = Variant(value, ShortValue)

def apply(value: Int): Variant = Variant(value, IntValue)

def apply(value: Long): Variant = Variant(value, LongValue)
}

case class Variant(value: Long, valueType: Variant.ValueType)

package variant

import variant.Variant.Unwrapper

object Main extends App with Unwrapper[Byte] {
val v = Variant(42.toByte)
val result = unwrap(v)
println(result)
}

I get the same exception using Scala 2.12.0

Looking at the Scala 2.12 and Scala 2.12.1 change logs I could not find anything that explains this behavior.

What's causing this and how can I get the code working with Scala 2.12.1?

Best regards,
Michael

Oliver Ruebenacker

unread,
Jan 3, 2017, 10:29:47 AM1/3/17
to Michael Thaler, scala-user

     Hello,

  Could you post the exception message and stack trace and mark where in the code it is thrown? Thanks!

     Best, Oliver

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oliver Ruebenacker
Senior Software Engineer, Diabetes Portal, Broad Institute

Seth Tisue

unread,
Jan 3, 2017, 10:55:45 AM1/3/17
to scala-user
shorter reproduction:

trait Unwrapper[@specialized(Byte) P] {
  def unwrap: P = 0L.asInstanceOf[P]
}
(new Unwrapper[Byte] {}).unwrap

Michael Thaler

unread,
Jan 4, 2017, 3:32:52 AM1/4/17
to scala-user, michael...@physik.tu-muenchen.de
Hi,


  Could you post the exception message and stack trace and mark where in the code it is thrown? Thanks!

The exception is:

 java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Byte
        at scala.runtime.BoxesRunTime.unboxToByte(BoxesRunTime.java:93)
        at variant.Variant$Unwrapper.unwrap$mcB$sp(Variant.scala:12)
        at variant.Variant$Unwrapper.unwrap$mcB$sp$(Variant.scala:12)
        at variant.Main$.unwrap$mcB$sp(Main.scala:5)
        at variant.Main$.delayedEndpoint$variant$Main$1(Main.scala:7)
        at variant.Main$delayedInit$body.apply(Main.scala:5)
        at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
        at scala.App.$anonfun$main$1$adapted(App.scala:76)
        at scala.collection.immutable.List.foreach(List.scala:378)
        at scala.App.main(App.scala:76)
        at scala.App.main$(App.scala:74)
        at variant.Main$.main(Main.scala:5)
        at variant.Main.main(Main.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:4

Best regards,
Michael

Michael Thaler

unread,
Jan 4, 2017, 3:33:33 AM1/4/17
to scala-user
Hi Seth,

thanks for the shorter example. I made it a bit longer to give some context.

Best regards,
Michael

Michael Thaler

unread,
Jan 9, 2017, 11:00:26 AM1/9/17
to scala-user
Hi,

I tried to investigate this further.

I compiled the following classes with Scala 2.11.8 and Scala 2.12.1:

trait Unwrapper[@specialized(Byte) P] {
def unwrap: P = {
val result = 0L.asInstanceOf[P]
result
}
}

object UnwrapperTest extends App with Unwrapper[Byte] {

println(unwrap)
}

With Scala 2.11.8 I get the following byte code:

$ javap -c Unwrapper

Compiled from "Unwrapper.scala"
public interface variant.Unwrapper{
public abstract java.lang.Object unwrap();

public abstract byte unwrap$mcB$sp();

}

$ javap -c Unwrapper\$class

Compiled from "Unwrapper.scala"
public abstract class variant.Unwrapper$class extends java.lang.Object{
public static java.lang.Object unwrap(variant.Unwrapper);
Code:
0: lconst_0
1: invokestatic #13; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long;
4: astore_1
5: aload_1
6: areturn

public static byte unwrap$mcB$sp(variant.Unwrapper);
Code:
0: aload_0
1: invokeinterface #24, 1; //InterfaceMethod variant/Unwrapper.unwrap:()Ljava/lang/Object;
6: invokestatic #28; //Method scala/runtime/BoxesRunTime.unboxToByte:(Ljava/lang/Object;)B
9: ireturn

public static void $init$(variant.Unwrapper);
Code:
0: return

}

$ javap -c Unwrapper\$mcB\$sp

Compiled from "Unwrapper.scala"
public interface variant.Unwrapper$mcB$sp extends variant.Unwrapper{
public abstract byte unwrap();

public abstract byte unwrap$mcB$sp();

}

$ javap -c Unwrapper\$mcB\$sp\$class

Compiled from "Unwrapper.scala"
public abstract class variant.Unwrapper$mcB$sp$class extends java.lang.Object{
public static byte unwrap(variant.Unwrapper$mcB$sp);
Code:
0: aload_0
1: invokeinterface #13, 1; //InterfaceMethod variant/Unwrapper$mcB$sp.unwrap$mcB$sp:()B
6: ireturn

public static byte unwrap$mcB$sp(variant.Unwrapper$mcB$sp);
Code:
0: lconst_0
1: l2i
2: i2b
3: istore_1
4: iload_1
5: ireturn

public static void $init$(variant.Unwrapper$mcB$sp);
Code:
0: return

}

With Scala 2.12.1 I get:

javap -c Unwrapper

public interface variant.Unwrapper{
public static java.lang.Object unwrap$(variant.Unwrapper);
Code:
0: aload_0
1: invokespecial #16; //InterfaceMethod unwrap:()Ljava/lang/Object;
4: areturn

public java.lang.Object unwrap();
Code:
0: lconst_0
1: invokestatic #23; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long;
4: astore_1
5: aload_1
6: areturn

public static byte unwrap$mcB$sp$(variant.Unwrapper);
Code:
0: aload_0
1: invokespecial #32; //InterfaceMethod unwrap$mcB$sp:()B
4: ireturn

public byte unwrap$mcB$sp();
Code:
0: aload_0
1: invokeinterface #16, 1; //InterfaceMethod unwrap:()Ljava/lang/Object;
6: invokestatic #36; //Method scala/runtime/BoxesRunTime.unboxToByte:(Ljava/lang/Object;)B
9: ireturn

public static void $init$(variant.Unwrapper);
Code:
0: return

javap -c Unwrapper\$mcB\$sp

public interface variant.Unwrapper$mcB$sp extends variant.Unwrapper{
}

I am not an expert on Java bytecode, but with Scala 2.11.8 I see the following byte code that converts a long to a byte:

public static byte unwrap$mcB$sp(variant.Unwrapper$mcB$sp);
Code:
0: lconst_0
1: l2i
2: i2b
3: istore_1
4: iload_1
5: ireturn

With Scala 2.12.1 I see no such code. Instead the following code is invoked:

public java.lang.Object unwrap();
Code:
0: lconst_0
1: invokestatic #23; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long;
4: astore_1
5: aload_1
6: areturn

This returns a boxed long. And then

public byte unwrap$mcB$sp();
Code:
0: aload_0
1: invokeinterface #16, 1; //InterfaceMethod unwrap:()Ljava/lang/Object;
6: invokestatic #36; //Method scala/runtime/BoxesRunTime.unboxToByte:(Ljava/lang/Object;)B
9: ireturn

And this unboxes the boxed long to a byte which does not work because BoxesRunTime.unboxToByte expects a java.lang.Byte, not a java.lang.Long.

This seems a compiler bug related to specializing, if I remove @specialized from

trait Unwrapper[@specialized(Byte) P] {
def unwrap: P = {
val result = 0L.asInstanceOf[P]
result
}
}


everything works fine. If my conclusions are correct, should I report this to scala-language or open a bug report for the Scala compiler?

Best regards,
Michael

Michael Thaler

unread,
Jan 9, 2017, 12:33:54 PM1/9/17
to scala-user
Actually the whole boxing / unboxing should not be there because the code is specialized.
Reply all
Reply to author
Forward
0 new messages