scala / bug

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

Boxed values are not checked when creating manifests #12945

Closed szeiger closed 4 months ago

szeiger commented 4 months ago

Reproduction steps

Scala version: Reproduced in 2.13.12 and 2.12.18

val o: Option[Long] = Option[Int](42).asInstanceOf[Option[Long]]

println(o.get.getClass) // prints "long"
val l = o.get // throws ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Long
      26: invokevirtual #59                 // Method o:()Lscala/Option;
      29: invokevirtual #65                 // Method scala/Option.get:()Ljava/lang/Object;
      32: getstatic     #70                 // Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
      35: invokevirtual #74                 // Method scala/reflect/ClassTag$.Long:()Lscala/reflect/ManifestFactory$LongManifest;
      38: invokevirtual #78                 // Method scala/runtime/ScalaRunTime$.anyValClass:(Ljava/lang/Object;Lscala/reflect/ClassTag;)Ljava/lang/Class;

Problem

While o.get is actually called (and the returned value apparently stays on the stack and is not discarded before the end of the method), the value is ignored because it's not needed for the primitive ClassTag. This also prevents it from throwing a ClassCastException.

Since getClass is defined on Any one could argue that no instance check should be expected at this point. What makes this corner case confusing is the fact that it returns the wrong manifest for the instance.

lrytz commented 4 months ago

I'd say this can be filed away under "we can't guarantee runtime behavior when using asInstanceOf". It's telling the compiler "believe me, this is an Option[Long]", so the compiler continues assuming it's an Option[Long].

som-snytt commented 4 months ago

That's a good one. I'm glad I'm not in a job interview where they ask how getClass is specified in Scala. I see that it is not specified, so it's not actually defined on Any.

On the one hand, I would expect the reflective operation to yield a truthful result.

Option("s").asInstanceOf[Option[C]].get.getClass // i.e., what is it "really"?

But the JavaDoc says it is bounded by the erasure of the static type (in Java) and there is no check.

TIL you can't write getClass[42]. I was going to joke that it means "The Class of '42" like a nostalgic war-era novel, but then realized that babies born this year are in their high school class of 2042. Did I do that math right?