From 828beb64bb86e98eed59ac1fb656fccc54d5ae19 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Wed, 13 Dec 2023 16:03:12 +0100 Subject: [PATCH 1/2] Relax comparison between Null and reference types in explicit nulls --- .../tools/dotc/transform/PatternMatcher.scala | 7 ++- .../dotty/tools/dotc/typer/Synthesizer.scala | 12 ----- .../reference/experimental/explicit-nulls.md | 24 ++------- tests/explicit-nulls/neg/equal1.scala | 33 +++++++----- tests/explicit-nulls/neg/equal2.scala | 13 +++-- tests/explicit-nulls/neg/flow-match.scala | 4 +- .../explicit-nulls/neg/flow-strip-null.scala | 22 ++++---- .../explicit-nulls/neg/pattern-matching.scala | 52 +++++++++++++++++++ .../anyref-equal-nulls.scala} | 30 +++++------ .../explicit-nulls/pos/pattern-matching.scala | 38 -------------- .../explicit-nulls/run/pattern-matching.scala | 37 +++++++++++++ .../unsafe-common/unsafe-match-null.scala | 11 ---- 12 files changed, 152 insertions(+), 131 deletions(-) create mode 100644 tests/explicit-nulls/neg/pattern-matching.scala rename tests/explicit-nulls/{unsafe-common/unsafe-equal.scala => pos/anyref-equal-nulls.scala} (56%) delete mode 100644 tests/explicit-nulls/pos/pattern-matching.scala create mode 100644 tests/explicit-nulls/run/pattern-matching.scala delete mode 100644 tests/explicit-nulls/unsafe-common/unsafe-match-null.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index a73bd0841957..1eb8353b7424 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -56,7 +56,12 @@ class PatternMatcher extends MiniPhase { if !inInlinedCode then // check exhaustivity and unreachability SpaceEngine.checkExhaustivity(tree) - SpaceEngine.checkRedundancy(tree) + // With explcit nulls, even if the selector type is non-nullable, + // we still need to consider the possibility of null value, + // so we use the after-erasure nullability for space operations + // to achieve consistent runtime behavior. + // For example, `val x: String = ???; x match { case null => }` should not be unreachable. + withoutMode(Mode.SafeNulls)(SpaceEngine.checkRedundancy(tree)) translated.ensureConforms(matchType) } diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index c94724faf4d4..b108205a32d5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -175,18 +175,6 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): cmpWithBoxed(cls1, cls2) else if cls2.isPrimitiveValueClass then cmpWithBoxed(cls2, cls1) - else if ctx.mode.is(Mode.SafeNulls) then - // If explicit nulls is enabled, and unsafeNulls is not enabled, - // we want to disallow comparison between Object and Null. - // If we have to check whether a variable with a non-nullable type has null value - // (for example, a NotNull java method returns null for some reasons), - // we can still cast it to a nullable type then compare its value. - // - // Example: - // val x: String = null.asInstanceOf[String] - // if (x == null) {} // error: x is non-nullable - // if (x.asInstanceOf[String|Null] == null) {} // ok - cls1 == defn.NullClass && cls1 == cls2 else if cls1 == defn.NullClass then cls1 == cls2 || cls2.derivesFrom(defn.ObjectClass) else if cls2 == defn.NullClass then diff --git a/docs/_docs/reference/experimental/explicit-nulls.md b/docs/_docs/reference/experimental/explicit-nulls.md index f8f9ac8e11be..7925bd5ea2b0 100644 --- a/docs/_docs/reference/experimental/explicit-nulls.md +++ b/docs/_docs/reference/experimental/explicit-nulls.md @@ -90,26 +90,10 @@ More details can be found in [safe initialization](../other-new-features/safe-in ## Equality -We don't allow the double-equal (`==` and `!=`) and reference (`eq` and `ne`) comparison between -`AnyRef` and `Null` anymore, since a variable with a non-nullable type cannot have `null` as value. -`null` can only be compared with `Null`, nullable union (`T | Null`), or `Any` type. - -For some reason, if we really want to compare `null` with non-null values, we have to provide a type hint (e.g. `: Any`). - -```scala -val x: String = ??? -val y: String | Null = ??? - -x == null // error: Values of types String and Null cannot be compared with == or != -x eq null // error -"hello" == null // error - -y == null // ok -y == x // ok - -(x: String | Null) == null // ok -(x: Any) == null // ok -``` +We still allow the double-equal (`==` and `!=`), reference (`eq` and `ne`) comparison, +and pattern matching between `Null` and reference types. +Even if a type is non-nullable, we still need to consider the possibility of `null` value +caused by the Java methods or uninitialized values. ## Java Interoperability diff --git a/tests/explicit-nulls/neg/equal1.scala b/tests/explicit-nulls/neg/equal1.scala index 3fab8ba4bf8a..45ec3084fd47 100644 --- a/tests/explicit-nulls/neg/equal1.scala +++ b/tests/explicit-nulls/neg/equal1.scala @@ -1,5 +1,8 @@ // Test what can be compared for equality against null. -class Foo { + +case class VC(x: Int) extends AnyVal + +def test = // Null itself val x0: Null = null x0 != x0 @@ -9,21 +12,21 @@ class Foo { null == null null != null - // Non-nullable types: error + // Non-nullable types: OK. val x1: String = "hello" - x1 != null // error - x1 == null // error - null == x1 // error - null != x1 // error - x1 == x0 // error - x0 != x1 // error - x1.asInstanceOf[String|Null] == null - x1.asInstanceOf[String|Null] == x0 + x1 != null + x1 == null + null == x1 + null != x1 + x1 == x0 + x0 != x1 + x1.asInstanceOf[String | Null] == null + x1.asInstanceOf[String | Null] == x0 x1.asInstanceOf[Any] == null x1.asInstanceOf[Any] == x0 // Nullable types: OK - val x2: String|Null = null + val x2: String | Null = null x2 == null null == x2 x2 == x0 @@ -41,4 +44,10 @@ class Foo { null == false // error 'a' == null // error null == 'b' // error -} + + // Nullable value types: OK. + val x3: Int | Null = null + x3 == null + null == x3 + x3 == x0 + x3 != x0 diff --git a/tests/explicit-nulls/neg/equal2.scala b/tests/explicit-nulls/neg/equal2.scala index 32cf21d918ac..299d29c103ab 100644 --- a/tests/explicit-nulls/neg/equal2.scala +++ b/tests/explicit-nulls/neg/equal2.scala @@ -1,5 +1,5 @@ -// Test that we can't compare for equality `null` with classes. -// This rule is for both regular classes and value classes. +// Test that we can compare values of regular classes against null, +// but not values of value classes. class Foo(x: Int) class Bar(x: Int) extends AnyVal @@ -7,12 +7,11 @@ class Bar(x: Int) extends AnyVal class Test { locally { val foo: Foo = new Foo(15) - foo == null // error: Values of types Null and Foo cannot be compared - null == foo // error - foo != null // error - null != foo // error + foo == null + null == foo + foo != null + null != foo - // To test against null, make the type nullable. val foo2: Foo | Null = foo // ok foo2 == null diff --git a/tests/explicit-nulls/neg/flow-match.scala b/tests/explicit-nulls/neg/flow-match.scala index e385758261cd..7a9d94adb90b 100644 --- a/tests/explicit-nulls/neg/flow-match.scala +++ b/tests/explicit-nulls/neg/flow-match.scala @@ -3,13 +3,13 @@ object MatchTest { def f6(s: String | Null): String = s match { case s2 => s2 // error - case null => "other" // error + case null => "other" case s3 => s3 } def f7(s: String | Null): String = s match { case null => "other" - case null => "other" // error + case null => "other" case s3 => s3 } } diff --git a/tests/explicit-nulls/neg/flow-strip-null.scala b/tests/explicit-nulls/neg/flow-strip-null.scala index 997d272d008f..7c79e1310f16 100644 --- a/tests/explicit-nulls/neg/flow-strip-null.scala +++ b/tests/explicit-nulls/neg/flow-strip-null.scala @@ -1,25 +1,21 @@ // Test we are correctly striping nulls from nullable unions. -class Foo { +class Foo: class B1 class B2 - locally { + + locally: val x: (Null | String) | Null | (B1 | (Null | B2)) = ??? - if (x != null) { + if x != null then val _: String | B1 | B2 = x // ok: can remove all nullable unions - } - } - locally { + locally: val x: (Null | String) & (Null | B1) = ??? - if (x != null) { + if x != null then val _: String & B1 = x // ok: can remove null from embedded intersection - } - } - locally { + locally: val x: (Null | B1) & B2 = ??? - if (x != null) {} // error: the type of x is not a nullable union, so we cannot remove the Null - } -} + if x != null then + val _: B1 & B2 = x // error: the type of x is not a nullable union, so we cannot remove the Null diff --git a/tests/explicit-nulls/neg/pattern-matching.scala b/tests/explicit-nulls/neg/pattern-matching.scala new file mode 100644 index 000000000000..38222b40b7f7 --- /dev/null +++ b/tests/explicit-nulls/neg/pattern-matching.scala @@ -0,0 +1,52 @@ +//> using options -Xfatal-warnings + +class Foo: + + val s: String = ??? + + s match + case s: String => 100 + case _ => 200 // error: unreachable case except for null + + s match + case s: String => 100 + case null => 200 + + s match + case null => 100 + case _ => 200 + + val s2: String | Null = ??? + + s2 match + case s2: String => 100 + case _ => 200 // error: unreachable case except for null + + s2 match + case s2: String => 100 + case null => 200 + + s2 match + case null => 200 + case s2: String => 100 + + sealed trait Animal + case class Dog(name: String) extends Animal + case object Cat extends Animal + + val a: Animal = ??? + a match + case Dog(name) => 100 + case Cat => 200 + case _ => 300 // error: unreachable case except for null + + val a2: Animal | Null = ??? + a2 match + case Dog(_) => 100 + case Cat => 200 + case _ => 300 // error: unreachable case except for null + + a2 match + case Dog(_) => 100 + case Cat => 200 + case null => 300 // ok diff --git a/tests/explicit-nulls/unsafe-common/unsafe-equal.scala b/tests/explicit-nulls/pos/anyref-equal-nulls.scala similarity index 56% rename from tests/explicit-nulls/unsafe-common/unsafe-equal.scala rename to tests/explicit-nulls/pos/anyref-equal-nulls.scala index 493aaebfbff2..f7609425e894 100644 --- a/tests/explicit-nulls/unsafe-common/unsafe-equal.scala +++ b/tests/explicit-nulls/pos/anyref-equal-nulls.scala @@ -11,10 +11,10 @@ class S { null == s1 null != s1 - s2 == null // error - s2 != null // error - null == s2 // error - null != s2 // error + s2 == null + s2 != null + null == s2 + null != s2 s1 == s2 s1 != s2 @@ -27,21 +27,21 @@ class S { null != n s1 == n - s2 == n // error + s2 == n n != s1 - n != s2 // error + n != s2 } locally { - ss1 == null // error - ss1 != null // error - null == ss1 // error - null != ss1 // error - - ss1 == n // error - ss1 != n // error - n == ss1 // error - n != ss1 // error + ss1 == null + ss1 != null + null == ss1 + null != ss1 + + ss1 == n + ss1 != n + n == ss1 + n != ss1 ss1 == ss2 ss2 != ss1 diff --git a/tests/explicit-nulls/pos/pattern-matching.scala b/tests/explicit-nulls/pos/pattern-matching.scala deleted file mode 100644 index 7e84fb8cd513..000000000000 --- a/tests/explicit-nulls/pos/pattern-matching.scala +++ /dev/null @@ -1,38 +0,0 @@ - -class Foo { - val s: String = ??? - s match { - case s: String => 100 // warning: type test will always succeed - case _ => 200 // warning: unreachable - } - - s match { - case s: String => 100 // warning: type test will always succeed - case _ => 200 // warning: unreachable - } - - sealed trait Animal - case class Dog(name: String) extends Animal - case object Cat extends Animal - - val a: Animal = ??? - a match { - case Dog(name) => 100 - case Cat => 200 - case _ => 300 // warning: unreachable - } - - val a2: Animal|Null = ??? - a2 match { - case Dog(_) => 100 - case Cat => 200 - case _ => 300 // warning: only matches null - } - - val a3: Animal|Null = ??? - a3 match { - case Dog(_) => 100 - case Cat => 200 - case null => 300 // ok - } -} diff --git a/tests/explicit-nulls/run/pattern-matching.scala b/tests/explicit-nulls/run/pattern-matching.scala new file mode 100644 index 000000000000..750eb747c386 --- /dev/null +++ b/tests/explicit-nulls/run/pattern-matching.scala @@ -0,0 +1,37 @@ +object Test: + + def main(args: Array[String]): Unit = + + val s: String = null.asInstanceOf[String] + + val r1 = s match + case s: String => 100 + case _ => 200 + assert(r1 == 200) + + val r2 = s match + case s: String => 100 + case null => 200 + assert(r2 == 200) + + val r3 = s match + case null => 100 + case _ => 200 + assert(r3 == 100) + + val s2: String | Null = null + + val r4 = s2 match + case s2: String => 100 + case _ => 200 + assert(r4 == 200) + + val r5 = s2 match + case s2: String => 100 + case null => 200 + assert(r5 == 200) + + val r6 = s2 match + case null => 200 + case s2: String => 100 + assert(r6 == 200) \ No newline at end of file diff --git a/tests/explicit-nulls/unsafe-common/unsafe-match-null.scala b/tests/explicit-nulls/unsafe-common/unsafe-match-null.scala deleted file mode 100644 index fc76de6ddd3a..000000000000 --- a/tests/explicit-nulls/unsafe-common/unsafe-match-null.scala +++ /dev/null @@ -1,11 +0,0 @@ -def test1 = - val s: String = ??? - s match - case _: String => - case null => // error: Values of types Null and String cannot be compared - -def test2 = - val s: String | Null = ??? - s match - case _: String => - case null => From 297bed4d339fced3f323a9f1eb44ff9092ae099f Mon Sep 17 00:00:00 2001 From: Seyon Sivatharan Date: Mon, 2 Jun 2025 22:06:16 -0400 Subject: [PATCH 2/2] Fix spacing, i21577.check, and warn comment changes --- .../tools/dotc/transform/patmat/Space.scala | 9 +-- tests/explicit-nulls/neg/flow-match.scala | 8 +-- .../explicit-nulls/run/pattern-matching.scala | 2 +- tests/explicit-nulls/warn/flow-match.check | 16 +++++ tests/explicit-nulls/warn/flow-match.scala | 16 +++++ tests/explicit-nulls/warn/i21577.check | 64 +++++++++---------- tests/explicit-nulls/warn/i21577.scala | 14 ++-- .../warn/pattern-matching.check | 24 +++++++ .../{neg => warn}/pattern-matching.scala | 10 +-- 9 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 tests/explicit-nulls/warn/flow-match.check create mode 100644 tests/explicit-nulls/warn/flow-match.scala create mode 100644 tests/explicit-nulls/warn/pattern-matching.check rename tests/explicit-nulls/{neg => warn}/pattern-matching.scala (79%) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index a079fd9c2afc..ab5885e6278c 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -969,12 +969,5 @@ object SpaceEngine { def checkMatch(m: Match)(using Context): Unit = if exhaustivityCheckable(m.selector) then checkExhaustivity(m) - - if reachabilityCheckable(m.selector) then - // With explicit nulls, even if the selector type is non-nullable, - // we still need to consider the possibility of null value, - // so we use the after-erasure nullability for space operations - // to achieve consistent runtime behavior. - // For example, `val x: String = ???; x match { case null => }` should not be unreachable. - withoutMode(Mode.SafeNulls)(checkReachability(m)) + if reachabilityCheckable(m.selector) then checkReachability(m) } diff --git a/tests/explicit-nulls/neg/flow-match.scala b/tests/explicit-nulls/neg/flow-match.scala index 7a9d94adb90b..cbf77bfd0196 100644 --- a/tests/explicit-nulls/neg/flow-match.scala +++ b/tests/explicit-nulls/neg/flow-match.scala @@ -3,13 +3,11 @@ object MatchTest { def f6(s: String | Null): String = s match { case s2 => s2 // error - case null => "other" - case s3 => s3 + case s3 => s3 // OK since not null } def f7(s: String | Null): String = s match { case null => "other" - case null => "other" - case s3 => s3 + case s3 => s3 // OK since not null } -} +} \ No newline at end of file diff --git a/tests/explicit-nulls/run/pattern-matching.scala b/tests/explicit-nulls/run/pattern-matching.scala index 750eb747c386..bc893f8f40e2 100644 --- a/tests/explicit-nulls/run/pattern-matching.scala +++ b/tests/explicit-nulls/run/pattern-matching.scala @@ -34,4 +34,4 @@ object Test: val r6 = s2 match case null => 200 case s2: String => 100 - assert(r6 == 200) \ No newline at end of file + assert(r6 == 200) diff --git a/tests/explicit-nulls/warn/flow-match.check b/tests/explicit-nulls/warn/flow-match.check new file mode 100644 index 000000000000..dc8d03881ffb --- /dev/null +++ b/tests/explicit-nulls/warn/flow-match.check @@ -0,0 +1,16 @@ +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/flow-match.scala:6:9 ------------------------------- +6 | case null => "other" // warn + | ^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/flow-match.scala:7:9 ------------------------------- +7 | case s3 => s3 // warn + | ^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/flow-match.scala:12:9 ------------------------------ +12 | case null => "other" // warn + | ^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/flow-match.scala:14:9 ------------------------------ +14 | case s4 => s4.nn // warn + | ^^ + | Unreachable case diff --git a/tests/explicit-nulls/warn/flow-match.scala b/tests/explicit-nulls/warn/flow-match.scala new file mode 100644 index 000000000000..e152da47bb41 --- /dev/null +++ b/tests/explicit-nulls/warn/flow-match.scala @@ -0,0 +1,16 @@ +// Test unreachable matches in presence of nulls + +object MatchTest2 { + def f6(s: String | Null): String = s match { + case s2 => s2.nn + case null => "other" // warn + case s3 => s3 // warn + } + + def f7(s: String | Null): String = s match { + case null => "other" + case null => "other" // warn + case s3: String => s3 + case s4 => s4.nn // warn + } +} \ No newline at end of file diff --git a/tests/explicit-nulls/warn/i21577.check b/tests/explicit-nulls/warn/i21577.check index 4bd8b87fae25..97f4ef346a43 100644 --- a/tests/explicit-nulls/warn/i21577.check +++ b/tests/explicit-nulls/warn/i21577.check @@ -1,32 +1,32 @@ --- [E121] Pattern Match Warning: tests\explicit-nulls\warn\i21577.scala:5:9 -------------------------------------------- -5 | case _ => // warn - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). --- [E121] Pattern Match Warning: tests\explicit-nulls\warn\i21577.scala:12:9 ------------------------------------------- -12 | case _ => // warn - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). --- [E121] Pattern Match Warning: tests\explicit-nulls\warn\i21577.scala:16:7 ------------------------------------------- -16 | case _ => // warn - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). --- [E121] Pattern Match Warning: tests\explicit-nulls\warn\i21577.scala:20:7 ------------------------------------------- -20 | case _ => // warn - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). --- [E029] Pattern Match Exhaustivity Warning: tests\explicit-nulls\warn\i21577.scala:29:27 ----------------------------- -29 |def f7(s: String | Null) = s match // warn - | ^ - | match may not be exhaustive. - | - | It would fail on pattern case: _: Null - | - | longer explanation available when compiling with `-explain` --- [E029] Pattern Match Exhaustivity Warning: tests\explicit-nulls\warn\i21577.scala:36:33 ----------------------------- -36 |def f9(s: String | Int | Null) = s match // warn - | ^ - | match may not be exhaustive. - | - | It would fail on pattern case: _: Int - | - | longer explanation available when compiling with `-explain` +-- [E121] Pattern Match Warning: tests/explicit-nulls/warn/i21577.scala:5:9 -------------------------------------------- +5 | case _ => // warn: null only + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E121] Pattern Match Warning: tests/explicit-nulls/warn/i21577.scala:12:9 ------------------------------------------- +12 | case _ => // warn: null only + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E121] Pattern Match Warning: tests/explicit-nulls/warn/i21577.scala:16:7 ------------------------------------------- +16 | case _ => // warn: null only + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/i21577.scala:20:7 ---------------------------------- +20 | case _ => // warn: unreachable + | ^ + | Unreachable case +-- [E029] Pattern Match Exhaustivity Warning: tests/explicit-nulls/warn/i21577.scala:29:27 ----------------------------- +29 |def f7(s: String | Null) = s match // warn: not exhaustive + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Null + | + | longer explanation available when compiling with `-explain` +-- [E029] Pattern Match Exhaustivity Warning: tests/explicit-nulls/warn/i21577.scala:36:33 ----------------------------- +36 |def f9(s: String | Int | Null) = s match // warn: not exhaustive + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Int + | + | longer explanation available when compiling with `-explain` \ No newline at end of file diff --git a/tests/explicit-nulls/warn/i21577.scala b/tests/explicit-nulls/warn/i21577.scala index 6691c15adb79..a4fc26f85f2b 100644 --- a/tests/explicit-nulls/warn/i21577.scala +++ b/tests/explicit-nulls/warn/i21577.scala @@ -2,22 +2,22 @@ def f(s: String) = val s2 = s.trim() s2 match case s3: String => - case _ => // warn + case _ => // warn: null only def f2(s: String | Null) = val s2 = s.nn.trim() s2 match case s3: String => - case _ => // warn + case _ => // warn: null only def f3(s: String | Null) = s match case s2: String => - case _ => // warn + case _ => // warn: null only def f5(s: String) = s match case _: String => - case _ => // warn + case _ => // warn: unreachable def f6(s: String) = s.trim() match case _: String => @@ -26,13 +26,13 @@ def f6(s: String) = s.trim() match def f61(s: String) = s.trim() match case _: String => -def f7(s: String | Null) = s match // warn +def f7(s: String | Null) = s match // warn: not exhaustive case _: String => def f8(s: String | Null) = s match case _: String => case null => -def f9(s: String | Int | Null) = s match // warn +def f9(s: String | Int | Null) = s match // warn: not exhaustive case _: String => - case null => \ No newline at end of file + case null => diff --git a/tests/explicit-nulls/warn/pattern-matching.check b/tests/explicit-nulls/warn/pattern-matching.check new file mode 100644 index 000000000000..b7b12b2a9215 --- /dev/null +++ b/tests/explicit-nulls/warn/pattern-matching.check @@ -0,0 +1,24 @@ +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/pattern-matching.scala:7:9 ------------------------- +7 | case _ => 200 // warn: unreachable + | ^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/pattern-matching.scala:11:9 ------------------------ +11 | case null => 200 // warn: unreachable + | ^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/pattern-matching.scala:14:9 ------------------------ +14 | case null => 100 // warn: unreachable + | ^^^^ + | Unreachable case +-- [E121] Pattern Match Warning: tests/explicit-nulls/warn/pattern-matching.scala:21:9 --------------------------------- +21 | case _ => 200 // warn: unreachable case except for null + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E030] Match case Unreachable Warning: tests/explicit-nulls/warn/pattern-matching.scala:39:9 ------------------------ +39 | case _ => 300 // warn: unreachable case except for null + | ^ + | Unreachable case +-- [E121] Pattern Match Warning: tests/explicit-nulls/warn/pattern-matching.scala:45:9 --------------------------------- +45 | case _ => 300 // warn: unreachable case except for null + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). diff --git a/tests/explicit-nulls/neg/pattern-matching.scala b/tests/explicit-nulls/warn/pattern-matching.scala similarity index 79% rename from tests/explicit-nulls/neg/pattern-matching.scala rename to tests/explicit-nulls/warn/pattern-matching.scala index 6e5053f6c197..f1cabc302c87 100644 --- a/tests/explicit-nulls/neg/pattern-matching.scala +++ b/tests/explicit-nulls/warn/pattern-matching.scala @@ -1,19 +1,17 @@ -//> using options -Xfatal-warnings - class Foo: val s: String = ??? s match case s: String => 100 - case _ => 200 // warn: unreachable case except for null + case _ => 200 // warn: unreachable s match case s: String => 100 - case null => 200 + case null => 200 // warn: unreachable s match - case null => 100 + case null => 100 // warn: unreachable case _ => 200 val s2: String | Null = ??? @@ -50,5 +48,3 @@ class Foo: case Dog(_) => 100 case Cat => 200 case null => 300 // ok - -// nopos-error: No warnings can be incurred under -Werror (or -Xfatal-warnings) \ No newline at end of file