-
Notifications
You must be signed in to change notification settings - Fork 21
ClassCastException with structural types, value classes #6336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Imported From: https://issues.scala-lang.org/browse/SI-6336?orig=1 |
@jsuereth said: |
@retronym said (edited on Sep 9, 2012 8:15:49 PM UTC): A play-by-play in the REPL: scala> class X[T](val i: T) extends AnyVal
defined class X
scala> object a { def y[T](x: X[T]) = x.i }
defined module a
scala> val t = a.y(new X(0))
t: Int = 0
scala> val t = (a: {def y[T](x: X[T]): T}).y(new X(0))
warning: there were 1 feature warnings; re-run with -feature for details
java.lang.ClassCastException: X cannot be cast to java.lang.Integer A very similar problem: methods with erased value classes as parameters can't be invoked with reflection. scala> class X[T](val i: T) extends AnyVal
defined class X
scala> object a { def y[T](x: X[T]) = x.i }
defined module a
scala> val cm = reflect.runtime.currentMirror
cm: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@4bcc946b of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [(memory)] and parent being scala.tools.nsc.util.ScalaClassLoader$URLClassLoader@642423ad of type class scala.tools.nsc.util.ScalaClassLoader$URLClassLoader with classpath [file:/Library/Java/JavaVirtualMachines/1.6.0_27-b07-395.jdk/Contents/Classes/classes.jar,file:/Library/Java/JavaVirtualMachines/1.6.0_27-b07-395.jdk/Contents/Classes/ui.jar,file:/Library/Java/JavaVirtualMachines/1.6.0_27-b07-395.jdk/Contents/Classes/jsse.jar,file:/Library/Java/JavaVirtualMachines/1.6.0_27-b07-395.jdk/Contents/Classes/jce.jar,file:/Library/Java/JavaVirtualMachines/1.6.0_27-b07-395.jdk/Contents/Class...
scala> val moduleA = cm.reflect(a)
moduleA: reflect.runtime.universe.InstanceMirror = instance mirror for a$@6ff64801
scala> val methodY = moduleA.symbol.typeSignature.declarations.last.asMethod
methodY: reflect.runtime.universe.MethodSymbol = method y
scala> moduleA.reflectMethod(methodY)
res10: reflect.runtime.universe.MethodMirror = method mirror for a.y[T](x: X[T]): T (bound to a$@6ff64801)
scala> res10(new X(0))
java.lang.NoClassDefFoundError: no Java class corresponding to ErasedValueType(X[T]) found
at scala.reflect.runtime.JavaMirrors$JavaMirror.typeToJavaClass(JavaMirrors.scala:1160) Currently, method signatures that accept value classes are compiled to accept the unboxed type: scala> class X(val i: Int) extends AnyVal
defined class X
scala> class C1 { def foo(x: X) = x.i * 2 }
defined class C1
scala> :javap C1
Compiled from "<console>"
public class C1 extends java.lang.Object{
public int foo(int);
public C1();
} The compiler unboxes the X at the call site (and eliminates allocation of a new X, where possible). But for reflective callers, this never happens. It seems to me that both problems would be solved if we left the signatures unmolested, and created a shadow method that accepts the unboxed value: public class C1 extends java.lang.Object{
public int foo(X)
public int foo$unboxed(int);
public C1();
} The first method would delegate to the second, and the compiler would endeavour to call the second directly where possible. This would parallel the way that a specialized method can still be called from a generic call site. It would also avoid double definition errors like: scala> class C3 { def foo(x: X) = x.i * 2; def foo(i: Int) = i * 2 }
<console>:11: error: double definition:
method foo:(i: Int)Int and
method foo:(x: X)Int at line 11
have same type after erasure: (i: Int)Int
class C3 { def foo(x: X) = x.i * 2; def foo(i: Int) = i * 2 }
^ |
@retronym said: |
@retronym said: scala> class ValueClass(val n: Int) extends AnyVal
defined class ValueClass
scala> def createStructural = new { | def withValueClass(valueClass: ValueClass) = () | }
createStructural: Object{def withValueClass(valueClass: ValueClass): Unit}
scala> createStructural.withValueClass(new ValueClass(1)) |
@jsuereth said: |
@retronym said: Martin's short-term suggestion on [scala-sips] is to disallow value classes in structural types. The followup might be to a) add boxed bridge methods outlined above; or b) make structural type dispatch / reflection aware of the necessary boxing/unboxing. |
@jsuereth said: |
@harrah said: object D {
def main(args: Array[String]) {
val a = new { def y[T](x: T): X[T] = new X(x) }
val t = a.y(3).i + 1
println(t)
}
}
class X[T](val i: T) extends AnyVal
|
@retronym said (edited on Sep 17, 2012 9:48:55 PM UTC): scala> class X[T](val i: T) extends AnyVal
defined class X
scala> (new { def y[T, XT <: X[T]](xt: XT) = xt.i }).y[Int, X[Int]](new X(0))
java.lang.VerifyError: (class: , method: <init> signature: ()V) Expecting to find object/array on stack UPDATE: This example work fine. The error I was seeing was from an older milestone, M5. |
@jsuereth said: |
@odersky said: |
A structural type with a value class parameter throws a ClassCastException at runtime.
The text was updated successfully, but these errors were encountered: