Skip to content

Commit 2793b75

Browse files
committed
Fix #3340: Always throw when casting to scala.Nothing
Also fix #4410
1 parent 732f7e2 commit 2793b75

File tree

6 files changed

+71
-0
lines changed

6 files changed

+71
-0
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ class Definitions {
570570
lazy val ClassClass: ClassSymbol = ctx.requiredClass("java.lang.Class")
571571
lazy val BoxedNumberClass: ClassSymbol = ctx.requiredClass("java.lang.Number")
572572
lazy val ClassCastExceptionClass: ClassSymbol = ctx.requiredClass("java.lang.ClassCastException")
573+
lazy val ClassCastExceptionClass_stringConstructor: TermSymbol = ClassCastExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
574+
case List(pt) => (pt isRef StringClass)
575+
case _ => false
576+
}).symbol.asTerm
573577
lazy val ArithmeticExceptionClass: ClassSymbol = ctx.requiredClass("java.lang.ArithmeticException")
574578
lazy val ArithmeticExceptionClass_stringConstructor: TermSymbol = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
575579
case List(pt) => (pt isRef StringClass)

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,13 @@ object TypeTestsCasts {
244244
else if (isDerivedValueClass(testCls)) {
245245
expr // adaptToType in Erasure will do the necessary type adaptation
246246
}
247+
else if (testCls eq defn.NothingClass) {
248+
// In the JVM `x.asInstanceOf[Nothing]` would throw a class cast exception except when `x eq null`.
249+
// To avoid this loophole we execute `x` and then regardless of the result throw a `ClassCastException`
250+
val throwCCE = Throw(New(defn.ClassCastExceptionClass.typeRef, defn.ClassCastExceptionClass_stringConstructor,
251+
Literal(Constant("Cannot cast to scala.Nothing")) :: Nil))
252+
Block(expr :: Nil, throwCCE).withSpan(expr.span)
253+
}
247254
else
248255
derivedTree(expr, defn.Any_asInstanceOf, testType)
249256
}

tests/pos/i828.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object X {
2+
val x: Int = null.asInstanceOf[Nothing]
3+
}

tests/run/i3340.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Test$.f1(i3340.scala:12)
2+
Test$.f2(i3340.scala:16)
3+
Test$.f3(i3340.scala:20)
4+
Test$.f4(i3340.scala:27)
5+
Test$.f5(i3340.scala:34)

tests/run/i3340.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
printlnStackLine(f1)
4+
printlnStackLine(f2)
5+
printlnStackLine(f3)
6+
printlnStackLine(f4)
7+
printlnStackLine(f5)
8+
}
9+
10+
def f1: Unit = {
11+
val a: Nothing =
12+
null.asInstanceOf[Nothing] // throws here
13+
}
14+
15+
def f2: Unit = {
16+
null.asInstanceOf[Nothing] // throws here
17+
}
18+
19+
def f3: Unit = {
20+
null.asInstanceOf[Nothing] // throws here
21+
()
22+
}
23+
24+
25+
def f4: Unit = {
26+
val n: Any = null
27+
n.asInstanceOf[Nothing] // throws here
28+
()
29+
}
30+
31+
def f5: Unit = {
32+
val n: Any = null
33+
val a: Nothing =
34+
n.asInstanceOf[Nothing] // throws here
35+
()
36+
}
37+
38+
def printlnStackLine(t: => Any): Unit = {
39+
try t
40+
catch {
41+
case e: ClassCastException =>
42+
println(e.getStackTrace.head)
43+
}
44+
}
45+
}

tests/run/i4410.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
val a =
3+
try null.asInstanceOf[Nothing]
4+
catch { case e: ClassCastException if e.getMessage == "Cannot cast to scala.Nothing" => /* As expected */ }
5+
def main(args: Array[String]): Unit = {
6+
}
7+
}

0 commit comments

Comments
 (0)