diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2332a9b8204e..79d0e4afedc7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -570,6 +570,10 @@ class Definitions { lazy val ClassClass: ClassSymbol = ctx.requiredClass("java.lang.Class") lazy val BoxedNumberClass: ClassSymbol = ctx.requiredClass("java.lang.Number") lazy val ClassCastExceptionClass: ClassSymbol = ctx.requiredClass("java.lang.ClassCastException") + lazy val ClassCastExceptionClass_stringConstructor: TermSymbol = ClassCastExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match { + case List(pt) => (pt isRef StringClass) + case _ => false + }).symbol.asTerm lazy val ArithmeticExceptionClass: ClassSymbol = ctx.requiredClass("java.lang.ArithmeticException") lazy val ArithmeticExceptionClass_stringConstructor: TermSymbol = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match { case List(pt) => (pt isRef StringClass) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index b0bac608048e..d4c1ac78cbde 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -244,6 +244,13 @@ object TypeTestsCasts { else if (isDerivedValueClass(testCls)) { expr // adaptToType in Erasure will do the necessary type adaptation } + else if (testCls eq defn.NothingClass) { + // In the JVM `x.asInstanceOf[Nothing]` would throw a class cast exception except when `x eq null`. + // To avoid this loophole we execute `x` and then regardless of the result throw a `ClassCastException` + val throwCCE = Throw(New(defn.ClassCastExceptionClass.typeRef, defn.ClassCastExceptionClass_stringConstructor, + Literal(Constant("Cannot cast to scala.Nothing")) :: Nil)) + Block(expr :: Nil, throwCCE).withSpan(expr.span) + } else derivedTree(expr, defn.Any_asInstanceOf, testType) } diff --git a/tests/pos/i828.scala b/tests/pos/i828.scala new file mode 100644 index 000000000000..77dc0d4901c3 --- /dev/null +++ b/tests/pos/i828.scala @@ -0,0 +1,3 @@ +object X { + val x: Int = null.asInstanceOf[Nothing] +} diff --git a/tests/run/i3340.check b/tests/run/i3340.check new file mode 100644 index 000000000000..bcb629b9186a --- /dev/null +++ b/tests/run/i3340.check @@ -0,0 +1,7 @@ +Test$.f1(i3340.scala:13) +Test$.f2(i3340.scala:17) +Test$.f3(i3340.scala:21) +Test$.f4(i3340.scala:28) +Test$.f5(i3340.scala:35) +foo +Test$.f6(i3340.scala:42) diff --git a/tests/run/i3340.scala b/tests/run/i3340.scala new file mode 100644 index 000000000000..328305cf03ae --- /dev/null +++ b/tests/run/i3340.scala @@ -0,0 +1,53 @@ +object Test { + def main(args: Array[String]): Unit = { + printlnStackLine(f1) + printlnStackLine(f2) + printlnStackLine(f3) + printlnStackLine(f4) + printlnStackLine(f5) + printlnStackLine(f6) + } + + def f1: Unit = { + val a: Nothing = + null.asInstanceOf[Nothing] // throws here + } + + def f2: Unit = { + null.asInstanceOf[Nothing] // throws here + } + + def f3: Unit = { + null.asInstanceOf[Nothing] // throws here + () + } + + + def f4: Unit = { + val n: Any = null + n.asInstanceOf[Nothing] // throws here + () + } + + def f5: Unit = { + val n: Any = null + val a: Nothing = + n.asInstanceOf[Nothing] // throws here + () + } + + def f6: Unit = { + val n: Any = null + val a: Nothing = + { println("foo"); n }.asInstanceOf[Nothing] // throws here + () + } + + def printlnStackLine(t: => Any): Unit = { + try t + catch { + case e: ClassCastException => + println(e.getStackTrace.head) + } + } +} diff --git a/tests/run/i4410.scala b/tests/run/i4410.scala new file mode 100644 index 000000000000..a123b60c7c87 --- /dev/null +++ b/tests/run/i4410.scala @@ -0,0 +1,7 @@ +object Test { + val a = + try null.asInstanceOf[Nothing] + catch { case e: ClassCastException if e.getMessage == "Cannot cast to scala.Nothing" => /* As expected */ } + def main(args: Array[String]): Unit = { + } +}