scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

isInstanceOf/asInstanceOf support for parametric polymorphism (manifest doesn't help and turns of unchecked warning which makes it worse) #5042

Closed scabug closed 12 years ago

scabug commented 13 years ago

At the moment isInstanceOf/asInstanceOf don't support (upper bounded) type parameters so it parametric polymorphism is not possible even if the type parameter has a manifest. All the manifest does is turning unchecked warnings off, there is still a class cast exception which is why a manifest solution makes the problem even worse.

In C# you can do just this and it works:

if (e is E) call((E)e);

but in Scala you cannot do this even with a typeparameter with manifest: if (e.isInstanceOf[E]) call(e.asInstanceOf[E])

Work around 1

if(isE(e) call(asE(e))
final def isE[F <: Exp: Manifest](e: F) = { manifest[E] <:< manifest[F] }
final def asE(e: Exp) = { val x = manifest[E].erasure.cast(e); println(x.getClass); x.asInstanceOf[E] }

is only turning of the unchecked warnings in hackish way

Work around 2

if (manifest[E].erasure.cast(e).isInstanceOf[E]) call(manifest[E].erasure.cast(e).asInstanceOf[E])

brings the unchecked warning it back so it is circular

Both workarounds generate a class cast exception so a manifest doesn't help much: java.lang.ClassCastException: Cannot cast impl.Lit to extension.EvalExp

See enclosed: expressionproblem.cs expressionproblem.scala

scabug commented 13 years ago

Imported From: https://issues.scala-lang.org/browse/SI-5042?orig=1 Reporter: DaveScala (davescala) Affected Versions: 2.9.1 Attachments:

scabug commented 12 years ago

@paulp said: No unchecked warnings, prints -5. You appear to have multiple misapprehensions about how things work at runtime. You're never calling getClass on an instance whose type neither the compiler nor you know; from such beginnings one cannot be too surprised at CCEs.

diff --git a/expressionproblem.scala b/expressionproblem.scala
index fe31f7037d..55d30688dc 100644
--- a/expressionproblem.scala
+++ b/expressionproblem.scala
@@ -11,8 +11,10 @@ package exprframework {

     abstract class Node[V <: Visitor: Manifest] extends Exp { 
         final def handle(v: Visitor) {
-            if (manifest[V].erasure.cast(v).isInstanceOf[V]) accept(manifest[V].erasure.cast(v).asInstanceOf[V])
-            else v.default(this)
+            if (manifest[V].erasure isAssignableFrom v.getClass)
+              accept(v.asInstanceOf[V])
+            else
+              v.default(this)
         }
         //final def isV[W <: Visitor: Manifest](v: W) = { manifest[V] <:< manifest[W] }
         //final def asV(v: Visitor) = { val x = manifest[V].erasure.cast(v); println(x.getClass); x.asInstanceOf[V] }
@@ -21,8 +23,10 @@ package exprframework {

     abstract class Op[E <: Exp: Manifest] extends Visitor { 
         final def apply(e: Exp) {
-            if (manifest[E].erasure.cast(e).isInstanceOf[E]) call(manifest[E].erasure.cast(e).asInstanceOf[E])
-            else e.handle(this)
+            if (manifest[E].erasure isAssignableFrom e.getClass)
+              call(e.asInstanceOf[E])
+            else
+              e.handle(this)
         }
         //final def isE[F <: Exp: Manifest](e: F) = { manifest[E] <:< manifest[F] }
         //final def asE(e: Exp) = { val x = manifest[E].erasure.cast(e); println(x.getClass); x.asInstanceOf[E] }
scabug commented 12 years ago

DaveScala (davescala) said: Thanks, that works and I even found a shorter one:

manifest[V].erasure isAssignableFrom v.getClass

can be written as

manifest[V].erasure.isInstance(v)