Skip to content

Commit cf6e7a9

Browse files
Merge pull request #5874 from dotty-staging/fix-#3340
Fix #3340: Always throw when casting to scala.Nothing
2 parents e4dd91f + 2b74394 commit cf6e7a9

File tree

6 files changed

+81
-0
lines changed

6 files changed

+81
-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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Test$.f1(i3340.scala:13)
2+
Test$.f2(i3340.scala:17)
3+
Test$.f3(i3340.scala:21)
4+
Test$.f4(i3340.scala:28)
5+
Test$.f5(i3340.scala:35)
6+
foo
7+
Test$.f6(i3340.scala:42)

tests/run/i3340.scala

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
printlnStackLine(f6)
9+
}
10+
11+
def f1: Unit = {
12+
val a: Nothing =
13+
null.asInstanceOf[Nothing] // throws here
14+
}
15+
16+
def f2: Unit = {
17+
null.asInstanceOf[Nothing] // throws here
18+
}
19+
20+
def f3: Unit = {
21+
null.asInstanceOf[Nothing] // throws here
22+
()
23+
}
24+
25+
26+
def f4: Unit = {
27+
val n: Any = null
28+
n.asInstanceOf[Nothing] // throws here
29+
()
30+
}
31+
32+
def f5: Unit = {
33+
val n: Any = null
34+
val a: Nothing =
35+
n.asInstanceOf[Nothing] // throws here
36+
()
37+
}
38+
39+
def f6: Unit = {
40+
val n: Any = null
41+
val a: Nothing =
42+
{ println("foo"); n }.asInstanceOf[Nothing] // throws here
43+
()
44+
}
45+
46+
def printlnStackLine(t: => Any): Unit = {
47+
try t
48+
catch {
49+
case e: ClassCastException =>
50+
println(e.getStackTrace.head)
51+
}
52+
}
53+
}

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)