Skip to content

Commit ca4aa16

Browse files
committed
Make isInstanceOf[Null] illegal everywhere
also add `scala.Singleton` to untestable types
1 parent bee809d commit ca4aa16

File tree

6 files changed

+16
-19
lines changed

6 files changed

+16
-19
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ class Definitions {
11961196

11971197
@tu lazy val topClasses: Set[Symbol] = Set(AnyClass, MatchableClass, ObjectClass, AnyValClass)
11981198

1199-
@tu lazy val untestableClasses: Set[Symbol] = Set(NothingClass, NullClass)
1199+
@tu lazy val untestableClasses: Set[Symbol] = Set(NothingClass, NullClass, SingletonClass)
12001200

12011201
@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
12021202
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,14 @@ object TypeTestsCasts {
236236

237237
val foundEffectiveClass = effectiveClass(expr.tpe.widen)
238238

239-
val isUntestable = defn.untestableClasses.contains(testCls)
240-
val isIllegalPrimitiveTest = foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass
241-
if isIllegalPrimitiveTest || isUntestable then
239+
if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then
242240
report.error(i"cannot test if value of $exprType is a reference of $testCls", tree.srcPos)
243241
false
244242
else foundClasses.exists(check)
245243
end checkSensical
246244

247245
if (expr.tpe <:< testType)
248-
if (expr.tpe.isNotNull || testType.widen.isRef(defn.NullClass)) {
246+
if (expr.tpe.isNotNull) {
249247
if (!inMatch) report.warning(TypeTestAlwaysSucceeds(expr.tpe, testType), tree.srcPos)
250248
constant(expr, Literal(Constant(true)))
251249
}
@@ -341,8 +339,14 @@ object TypeTestsCasts {
341339
case AppliedType(tref: TypeRef, _) if tref.symbol == defn.PairClass =>
342340
ref(defn.RuntimeTuples_isInstanceOfNonEmptyTuple).appliedTo(expr)
343341
case _ =>
344-
val erasedTestType = erasure(testType)
345-
transformIsInstanceOf(expr, erasedTestType, erasedTestType, flagUnrelated)
342+
val testWidened = testType.widen
343+
defn.untestableClasses.find(testWidened.isRef(_)) match
344+
case Some(untestable) =>
345+
report.error(i"$untestable cannot be used in runtime type tests", tree.srcPos)
346+
constant(expr, Literal(Constant(false)))
347+
case _ =>
348+
val erasedTestType = erasure(testType)
349+
transformIsInstanceOf(expr, erasedTestType, erasedTestType, flagUnrelated)
346350
}
347351

348352
if (sym.isTypeTest) {
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
def foo1[T <: Matchable](t: T) = t match { case t: Null => () }
2-
31
def foo2[T <: Matchable](t: T) = t match { case null => () }

tests/neg/i4004.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
@main def Test =
22
"a".isInstanceOf[Null] // error
3-
null.isInstanceOf[Null]
3+
null.isInstanceOf[Null] // error
44
"a".isInstanceOf[Nothing] // error
5+
"a".isInstanceOf[Singleton] // error
56

67
"a" match
78
case _: Null => () // error
9+
case _: Nothing => () // error
10+
case _: Singleton => () // error
811
case _ => ()
912

1013
null match
11-
case _: Null => ()
14+
case _: Null => () // error
1215
case _ => ()
1316

tests/run/i4004.check

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/run/i4004.scala

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)