Skip to content
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

Type inference fail with Array produces ClassCastException at runtime #5353

Open
scabug opened this issue Jan 2, 2012 · 24 comments
Open

Type inference fail with Array produces ClassCastException at runtime #5353

scabug opened this issue Jan 2, 2012 · 24 comments

Comments

@scabug
Copy link

@scabug scabug commented Jan 2, 2012

scala> if (false) Array("qwe") else Array()
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

Expected: either compilation error, or correct inference for the second array creation

@scabug
Copy link
Author

@scabug scabug commented Jan 2, 2012

Imported From: https://issues.scala-lang.org/browse/SI-5353?orig=1
Reporter: @OlegYch
Affected Versions: 2.9.1, 2.10.0

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2012

@paulp said:
Here is another situation which generates CCEs. Array lubs can't be taken this way.

object Test {
  def main(args: Array[String]): Unit = List(Array(0), Array(0L)).map(x => x)
}
// java.lang.ClassCastException: [I cannot be cast to [Ljava.lang.Object;

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2012

@paulp said:
Looks like mine is a regression from 2.9.1. The one first reported in the ticket is in 2.9.1 as well as trunk.

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2012

@paulp said:
I think the regression is related to varargs changes, and the fact that Arrays are being passed to a varargs method as Array elements and not as collections of elements is confusing to scalac.

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2012

@paulp said:
This may be the same as #4216.

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2012

@paulp said:
After fiddling with this for far too long, all I can say is we need to completely reevaluate this aspect of the compiler. It should not be this hard to avoid generating CCEs. I think we throw away too much information, and should be able to access the actual types of arrays during genjvm with no uncertainty.

@scabug
Copy link
Author

@scabug scabug commented May 20, 2012

@retronym said:
Since ArrayTags have landed:

scala> Array()
<console>:8: error: macro has not been expanded
              Array()
                   ^

scala> :type if (false) Array("qwe") else Array()
<console>:8: error: macro has not been expanded
       if (false) Array("qwe") else Array()
                                         ^
<console>:8: error: macro has not been expanded
              if (false) Array("qwe") else Array()

The error message isn't stellar, but it beats a runtime error.

@scabug
Copy link
Author

@scabug scabug commented Jun 19, 2012

@retronym said (edited on Jun 19, 2012 9:30:02 PM UTC):
This behaves well with 2.10.0-20120619-072829-f1684e9edf

scala> List(Array(0), Array(0L)).map(x => x)bad option: '-nc'

res0: List[Array[_ >: Long with Int <: AnyVal]] = List(Array(0), Array(0))

With 2.10.0-M3 I saw:

~/code/scala ~/usr/scala-2.10.0-M3/bin/scala -nocompdaemon
Welcome to Scala version 2.10.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_31).
Type in expressions to have them evaluated.
Type :help for more information.

scala> List(Array(0), Array(0L)).map(x => x)
java.lang.AbstractMethodError
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:241)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:241)
	at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
	at scala.collection.immutable.List.foreach(List.scala:77)
	at scala.collection.TraversableLike$class.map(TraversableLike.scala:241)

@scabug
Copy link
Author

@scabug scabug commented Jun 19, 2012

@paulp said:
Not well enough (same as below with m4.)

% scala
Welcome to Scala version 2.10.0-20120619-042542-32d42b1264 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_04).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def f = List(Array(0), Array(0L)).map(x => x)
f: List[Array[_ >: Long with Int <: AnyVal]]

scala> f(0)(0).getClass
java.lang.ClassCastException: java.lang.Integer cannot be cast to scala.AnyVal
	at .<init>(<console>:9)
	at .<clinit>(<console>)
	at .<init>(<console>:7)
	at .<clinit>(<console>)
	at $print(<console>)

@scabug
Copy link
Author

@scabug scabug commented Jul 4, 2012

@paulp said:
Well, that failure has a whole nothing to do with this ticket.

scala> 5.getClass
res0: Class[Int] = int

scala> (5: AnyVal).getClass
java.lang.ClassCastException: java.lang.Integer cannot be cast to scala.AnyVal

scala> (5: Any).getClass
res1: Class[_] = class java.lang.Integer

@scabug
Copy link
Author

@scabug scabug commented Jul 19, 2012

@odersky said:
It's OK that we fail this at compile time now, but the error message could indeed be better. Reassigning to Eugene for that one.

The getClass failure should be a separate ticket. Paul, I assume you have that on the radar already?

@scabug
Copy link
Author

@scabug scabug commented Jul 19, 2012

@paulp said:
I made it #6108.

@scabug
Copy link
Author

@scabug scabug commented Jan 22, 2013

@adriaanm said (edited on Jan 22, 2013 11:35:22 PM UTC):
fixed in 2.11.0-M1 by http://github.com/scala/scala/issues/1430

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@xeno-by said:
Looks like the underlying problem isn't solved, it's just masked by #5923. As soon as the latter was fixed (scala/scala#2464), the troubles manifested themselves again.

scala> lub(List(typeOf[Array[String]], typeOf[Array[Nothing]]))
res2: reflect.runtime.universe.Type = Array[_ <: String]

I wonder how to reconcile this behavior of lub with the fact that arrays of Nothing are represented as arrays of Objects at runtime.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said (edited by @xeno-by on Apr 28, 2013 3:54:49 PM UTC):
Transplanting my email to scala-internals: http://groups.google.com/group/scala-internals/browse_thread/thread/ee903d0a2883deef

Here is the issue with Array lubs:

For a type CC[T] where T is invariant, the lub of CC[A] and CC[B] should be

  CC[T] forSome { type T >: glb(A, B) <: lub(A, B) }.

But if A=Nothing, that is simplified to

  CC[T] forSome { type T <: B }

You can usually get away with this. Take Set for instance. If you make a
list with a Set[Nothing] and a Set[Int] in it, it will appear as a Set[_ <: Int].
Since you can't get any values out of a Set[Nothing], nor put
any into it, erasure protects you from the lie being revealed. There is
nothing but a "Set" there under the hood.

With Arrays, there is storage and the storage has a type which is
stronger than "Array". That means if you discard any information when
taking the lub, you risk failing at runtime. The lub of Array[Nothing]
and Array[Int] has to be

  Array[T] forSome { type T <: Int with Nothing }

In other words it cannot simplify "Int with Nothing" to "Int" as it
does at present. It is not safe to replace "A with B" with "A" in an
existential bound, even when B is a subtype of A.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said:
And now having written that I think this issue might simplify to: an Array with an existential element type can only be represented with AnyRef.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said:
I think I know where best to fix this.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@xeno-by said:
How about modifying Erasure > unboundedGenericArrayLevel?

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said (edited by @xeno-by on Apr 28, 2013 8:44:14 PM UTC):
My belief is it must be fixed here, like so: https://github.com/paulp/scala/compare/issue;5353.
upd. Later on Paul has submitted a pull request based on the aforementioned sketch: scala/scala#2466.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@xeno-by said (edited on Apr 28, 2013 8:52:00 PM UTC):
Still broken:

Welcome to Scala version 2.10.2-20130425-053916-403ba8938f (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> if (false) Array("qwe") else Array()
<console>:8: error: this type parameter must be specified
              if (false) Array("qwe") else Array()
                                                ^

scala> if (false) Array("qwe") else Array[Nothing]()
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

@scabug
Copy link
Author

@scabug scabug commented Apr 30, 2013

@adriaanm said:
Array[T] where T <: Nothing or T <: Null must be ruled out

@scabug
Copy link
Author

@scabug scabug commented Apr 30, 2013

@paulp said:
Adriaan, where do you think it should happen? I have found it difficult.

@scabug
Copy link
Author

@scabug scabug commented May 1, 2013

@retronym said:
How about cutting if off in the inputs to lub, and then more generally in a tree traversal in refchecks?

@scabug
Copy link
Author

@scabug scabug commented May 2, 2013

@paulp said:
I stopped at the comma; scala/scala#2486

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants