diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index fb8bd917ca20..60121f5fef6c 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1005,15 +1005,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (!ctx.erasedTypes) cast(tp) else Erasure.Boxing.adaptToType(tree, tp) + /** `tree eq null` (might need a cast to be type correct) */ + def testNull(using Context): Tree = nullTest(defn.Object_eq) + /** `tree ne null` (might need a cast to be type correct) */ - def testNotNull(using Context): Tree = { + def testNotNull(using Context): Tree = nullTest(defn.Object_ne) + + private def nullTest(op: Symbol)(using Context) = val receiver = if (tree.tpe.isBottomType) // If the receiver is of type `Nothing` or `Null`, add an ascription so that the selection // succeeds: e.g. `null.ne(null)` doesn't type, but `(null: AnyRef).ne(null)` does. Typed(tree, TypeTree(defn.AnyRefType)) else tree.ensureConforms(defn.ObjectType) - receiver.select(defn.Object_ne).appliedTo(nullLiteral).withSpan(tree.span) - } + receiver.select(op).appliedTo(nullLiteral).withSpan(tree.span) /** If inititializer tree is `_`, the default value of its type, * otherwise the tree itself. diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index dc672690702c..86a3344bcccb 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1256,7 +1256,7 @@ 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 untestableClasses: Set[Symbol] = Set(NothingClass, SingletonClass) @tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0).asInstanceOf[Array[TypeRef]] val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass)) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index c2367e6bfde0..44f40fb4b7e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -245,9 +245,10 @@ object TypeTestsCasts { if (expr.tpe <:< testType) && inMatch then if expr.tpe.isNotNull then constant(expr, Literal(Constant(true))) + else if testCls == defn.NullClass then expr.testNull else expr.testNotNull else { - if expr.tpe.isBottomType then + if expr.tpe.widen.isNothingType then report.warning(TypeTestAlwaysDiverges(expr.tpe, testType), tree.srcPos) val nestedCtx = ctx.fresh.setNewTyperState() val foundClsSyms = foundClasses(expr.tpe.widen, Nil) @@ -263,6 +264,8 @@ object TypeTestsCasts { case _ => transformIsInstanceOf( expr, defn.boxedType(testCls.typeRef), testCls.typeRef, flagUnrelated) + else if testCls == defn.NullClass then + expr.testNull else derivedTree(expr, defn.Any_isInstanceOf, testType) } @@ -370,5 +373,5 @@ object TypeTestsCasts { case _ => EmptyTree } interceptWith(expr) - } + }//.showing(i"intercept $tree -> $result") } diff --git a/tests/neg/i4004.scala b/tests/neg/i4004.scala index bf757a0863a7..07afad0ce8a9 100644 --- a/tests/neg/i4004.scala +++ b/tests/neg/i4004.scala @@ -1,6 +1,4 @@ @main def Test = - "a".isInstanceOf[Null] // error - null.isInstanceOf[Null] // error "a".isInstanceOf[Nothing] // error "a".isInstanceOf[Singleton] // error @@ -9,8 +7,3 @@ case _: Nothing => () // error case _: Singleton => () // error case _ => () - - null match - case _: Null => () // error - case _ => () - diff --git a/tests/pos/i14896.scala b/tests/pos/i14896.scala new file mode 100644 index 000000000000..2ef595fbcaa4 --- /dev/null +++ b/tests/pos/i14896.scala @@ -0,0 +1,2 @@ +object Ex { def unapply(p: Any): Option[_ <: Int] = null } +object Foo { val Ex(_) = null: @unchecked } \ No newline at end of file diff --git a/tests/run/i4004.scala b/tests/run/i4004.scala new file mode 100644 index 000000000000..96ed375b13f9 --- /dev/null +++ b/tests/run/i4004.scala @@ -0,0 +1,15 @@ +@main def Test = + assert(!("a": Any).isInstanceOf[Null]) + assert(null.isInstanceOf[Null]) + assert((null: Any).isInstanceOf[Null]) + + ("a": Any) match + case _: Null => assert(false) + case _ => + + null match + case _: Null => () + + (null: Any) match + case _: Null => () +