diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 547ceb292055..2fec0683c410 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1196,6 +1196,8 @@ class Definitions { @tu lazy val topClasses: Set[Symbol] = Set(AnyClass, MatchableClass, ObjectClass, AnyValClass) + @tu lazy val untestableClasses: Set[Symbol] = Set(NothingClass, NullClass, SingletonClass) + @tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0) val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass)) def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 5d03d5381eed..a4951b99f599 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -339,8 +339,14 @@ object TypeTestsCasts { case AppliedType(tref: TypeRef, _) if tref.symbol == defn.PairClass => ref(defn.RuntimeTuples_isInstanceOfNonEmptyTuple).appliedTo(expr) case _ => - val erasedTestType = erasure(testType) - transformIsInstanceOf(expr, erasedTestType, erasedTestType, flagUnrelated) + val testWidened = testType.widen + defn.untestableClasses.find(testWidened.isRef(_)) match + case Some(untestable) => + report.error(i"$untestable cannot be used in runtime type tests", tree.srcPos) + constant(expr, Literal(Constant(false))) + case _ => + val erasedTestType = erasure(testType) + transformIsInstanceOf(expr, erasedTestType, erasedTestType, flagUnrelated) } if (sym.isTypeTest) { diff --git a/tests/explicit-nulls/pos/matchable.scala b/tests/explicit-nulls/pos/matchable.scala index 928f8e33934f..ef9c5e7eae07 100644 --- a/tests/explicit-nulls/pos/matchable.scala +++ b/tests/explicit-nulls/pos/matchable.scala @@ -1,3 +1 @@ -def foo1[T <: Matchable](t: T) = t match { case t: Null => () } - def foo2[T <: Matchable](t: T) = t match { case null => () } \ No newline at end of file diff --git a/tests/neg/i4004.scala b/tests/neg/i4004.scala new file mode 100644 index 000000000000..bf757a0863a7 --- /dev/null +++ b/tests/neg/i4004.scala @@ -0,0 +1,16 @@ +@main def Test = + "a".isInstanceOf[Null] // error + null.isInstanceOf[Null] // error + "a".isInstanceOf[Nothing] // error + "a".isInstanceOf[Singleton] // error + + "a" match + case _: Null => () // error + case _: Nothing => () // error + case _: Singleton => () // error + case _ => () + + null match + case _: Null => () // error + case _ => () +