Here are two REPL sessions (inspired by this question, although my question is different):
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def ignore(it: String) = 42
ignore: (it: String)Int
scala> ignore(null.asInstanceOf[Nothing])
res0: Int = 42
And:
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def ignore(it: String) = 42
ignore: (it: String)Int
scala> ignore(null.asInstanceOf[Nothing])
java.lang.NullPointerException
at .<init>(<console>:9)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
The only difference is that the first is Scala 2.9.2 and the second is 2.10.0.
Can someone point to the changes in 2.10 that lead to this new behavior?
I know that casting to Nothing
is a silly thing to do, and that the answer might be "this is all undefined behavior so just stop doing that", but it looks like the kind of thing that could potentially have implications for upgraders, and I don't remember running into any discussions of changes that would explain this.
Since Scala treats null
differently from the None
case on an option, even a null
value of Nothing
is problematic--there should be exactly zero instances of Nothing
, not one instance that may or may not break depending on how you use it.
Thus, I cannot see how the old behavior is anything but a bug. It should be mentined in the release notes that it was fixed, but relying on .asInstanceOf[Nothing]
to do anything save throw an exception is sufficiently contrary to type-sanity that I don't think anything more is needed. (In fact, I don't even think the release note is needed.)
This looks like an issue just with the console, not with the language. If you run this small application which invokes the exact same method, scala 2.10 doesn't have a problem with it.
object Test extends App {
override def main(args: Array[String]) {
println(takesString(null.asInstanceOf[Nothing]))
}
def takesString(a: String) = 42
}
To simplify your example from above, you could just type
null.asInstanceOf[Nothing]
and the console would give you the same error. I presume it has something to do with printing out the type.
Update: Looks like I accidentally ran against 2.9.2. Still fails as a script in 2.10 RC5 as the author points out in comment.
I know you are not expecting the answer "this is all undefined behavior, so (...)", but when you add "thing that could potentially have implications for upgraders", I must remember (even if it's obvious) that people can't rely or expect anything of the result of a thing that has undefined behavior, by its own definition.
In the specific case you mentioned, I don't think it is undefined behavior: it should throw an exception. Nothing
is a subclass of Null
, not the other way around - my first expectation, without testing, was that the line null.asInstanceOf[Nothing]
would throw a ClassCastException
, as null
is not a Nothing
. However, you can see that null
is a special instance (as it is in Java). Try running:
scala> "aaa".asInstanceOf[Nothing]
java.lang.ClassCastException: java.lang.String cannot be cast to scala.runtime.N
othing$
at .<init>(<console>:8)
at .<clinit>(<console>)
My guess is that it happens because internally, obj.asInstanceOf[T]
calls obj.getClass()
in order to check the cast at runtime. As calling any method on null
throws a NullPointerException
, that exception is thrown before ClassCastException
.
Back to your specific question, it seems that Scala 2.9.2 handles in a (very) special way that specific case. Running a few more tests:
scala> ignore(3.asInstanceOf[String])
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stri
ng
at .<init>(<console>:9)
at .<clinit>(<console>)
scala> ignore({ println("test"); "aaa" })
test
res6: Int = 42
You can see that the argument is always being evaluated, save for your case. Scala 2.10 definitely has the most consistent behavior. However, this issue should not affect any developer upgrading to Scala 2.10; I can't see any case where obj.asInstanceOf[Nothing]
is correct code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With