Skip to content

Commit 5061804

Browse files
authored
Merge pull request #13976 from dotty-staging/unsafe-nulls-fix-space
Fix case null on non-nullable type in unsafe nulls
2 parents 62d54fe + d296dd4 commit 5061804

File tree

6 files changed

+39
-21
lines changed

6 files changed

+39
-21
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ object SymDenotations {
813813
/** Is this symbol a class of which `null` is a value? */
814814
final def isNullableClass(using Context): Boolean =
815815
if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes
816-
then symbol == defn.NullClass || symbol == defn.AnyClass
816+
then symbol == defn.NullClass || symbol == defn.AnyClass || symbol == defn.MatchableClass
817817
else isNullableClassAfterErasure
818818

819819
/** Is this symbol a class of which `null` is a value after erasure?

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -330,24 +330,15 @@ class SpaceEngine(using Context) extends SpaceLogic {
330330

331331
private val constantNullType = ConstantType(Constant(null))
332332

333-
/** Does the given tree stand for the literal `null`? */
334-
def isNullLit(tree: Tree): Boolean = tree match {
335-
case Literal(Constant(null)) => true
336-
case _ => false
337-
}
338-
339333
override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = trace(s"atomic intersection: ${AndType(tp1, tp2).show}", debug) {
340334
// Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1).
341-
if (!ctx.explicitNulls && (tp1.isNullType || tp2.isNullType)) {
335+
if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then
342336
// Since projections of types don't include null, intersection with null is empty.
343337
Empty
344-
}
345-
else {
338+
else
346339
val res = TypeComparer.provablyDisjoint(tp1, tp2)
347-
348-
if (res) Empty
340+
if res then Empty
349341
else Typ(AndType(tp1, tp2), decomposed = true)
350-
}
351342
}
352343

353344
/** Return the space that represents the pattern `pat` */
@@ -549,7 +540,8 @@ class SpaceEngine(using Context) extends SpaceLogic {
549540

550541
/** Is `tp1` a subtype of `tp2`? */
551542
def isSubType(tp1: Type, tp2: Type): Boolean = trace(i"$tp1 <:< $tp2", debug, show = true) {
552-
if tp1 == constantNullType && !ctx.explicitNulls then tp2 == constantNullType
543+
if tp1 == constantNullType && !ctx.mode.is(Mode.SafeNulls)
544+
then tp2 == constantNullType
553545
else adaptType(tp1, tp2) <:< tp2
554546
}
555547

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class CompilationTests {
247247
aggregateTests(
248248
compileFilesInDir("tests/explicit-nulls/pos", explicitNullsOptions),
249249
compileFilesInDir("tests/explicit-nulls/pos-separate", explicitNullsOptions),
250+
compileFilesInDir("tests/explicit-nulls/pos-patmat", explicitNullsOptions and "-Xfatal-warnings"),
250251
compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions and "-language:unsafeNulls"),
251252
)
252253
}.checkCompile()

tests/explicit-nulls/neg-patmat/patmat1.scala renamed to tests/explicit-nulls/neg-patmat/match-pat.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
class Foo {
2+
23
val s: String = ???
3-
s match {
4-
case s: String => 100 // warning: type test will always succeed
5-
case _ => 200 // error: unreachable
6-
}
74

85
s match {
9-
case s: String => 100 // warning: type test will always succeed
6+
case s: String => 100
107
case _ => 200 // error: unreachable
118
}
129

@@ -15,20 +12,23 @@ class Foo {
1512
case object Cat extends Animal
1613

1714
val a: Animal = ???
15+
1816
a match {
1917
case Dog(name) => 100
2018
case Cat => 200
2119
case _ => 300 // error: unreachable
2220
}
2321

24-
val a2: Animal|Null = ???
22+
val a2: Animal | Null = ???
23+
2524
a2 match {
2625
case Dog(_) => 100
2726
case Cat => 200
2827
case _ => 300
2928
}
3029

31-
val a3: Animal|Null = ???
30+
val a3: Animal | Null = ???
31+
3232
a3 match {
3333
case Dog(_) => 100
3434
case Cat => 200
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.language.unsafeNulls
2+
3+
def test1 =
4+
val s: String = ???
5+
s match
6+
case _: String =>
7+
// under unsafeNulls, we should not get Match case Unreachable Warning
8+
case null => // ok
9+
10+
def test2 =
11+
val s: String | Null = ???
12+
s match
13+
case _: String =>
14+
case null =>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def test1 =
2+
val s: String = ???
3+
s match
4+
case _: String =>
5+
case null => // error: Values of types Null and String cannot be compared
6+
7+
def test2 =
8+
val s: String | Null = ???
9+
s match
10+
case _: String =>
11+
case null =>

0 commit comments

Comments
 (0)