diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index f1b74d3fb67c..51cdb7ec209d 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -113,6 +113,7 @@ object SpaceEngine { def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b) def canDecompose(typ: Typ)(using Context): Boolean = typ.canDecompose def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose + def nullSpace(using Context): Space = Typ(ConstantType(Constant(null)), decomposed = false) /** Simplify space such that a space equal to `Empty` becomes `Empty` */ def computeSimplify(space: Space)(using Context): Space = trace(i"simplify($space)")(space match { @@ -690,7 +691,6 @@ object SpaceEngine { else NoType }.filter(_.exists) parts - case _ => ListOfNoType end rec @@ -904,6 +904,10 @@ object SpaceEngine { then project(OrType(selTyp, ConstantType(Constant(null)), soft = false)) else project(selTyp) ) + def projectPat(pat: Tree): Space = + // Project toplevel wildcard pattern to nullable + if isNullable && isWildcardArg(pat) then Or(project(pat) :: nullSpace :: Nil) + else project(pat) var i = 0 val len = cases.length @@ -913,7 +917,7 @@ object SpaceEngine { while (i < len) { val CaseDef(pat, guard, _) = cases(i) - val curr = trace(i"project($pat)")(project(pat)) + val curr = trace(i"project($pat)")(projectPat(pat)) val covered = trace("covered")(simplify(intersect(curr, targetSpace))) diff --git a/tests/patmat/null.check b/tests/patmat/null.check index d9c265adf377..36d64f44e2d2 100644 --- a/tests/patmat/null.check +++ b/tests/patmat/null.check @@ -1,3 +1,4 @@ 6: Pattern Match 13: Pattern Match 20: Pattern Match +21: Match case Unreachable diff --git a/tests/patmat/null.scala b/tests/patmat/null.scala index b918109c0cc5..9cff29a5c4e8 100644 --- a/tests/patmat/null.scala +++ b/tests/patmat/null.scala @@ -18,5 +18,6 @@ class Test { case Some(null) => case None => case y => + case _ => } } \ No newline at end of file diff --git a/tests/warn/i15503d.scala b/tests/warn/i15503d.scala index 9a0ba9b2f8dc..a45e89c3af59 100644 --- a/tests/warn/i15503d.scala +++ b/tests/warn/i15503d.scala @@ -16,7 +16,7 @@ val a = Sum(S(S(Z)),Z) match { case Sum(a@S(b@S(_)), Z) => a // warn case Sum(a@S(b@(S(_))), Z) => Sum(a,b) // warn unreachable case Sum(_,_) => Z // OK - case _ => Z // warn unreachable + case _ => Z } // todo : This should pass in the future diff --git a/tests/warn/i20121.scala b/tests/warn/i20121.scala new file mode 100644 index 000000000000..ce8e3e4d74f6 --- /dev/null +++ b/tests/warn/i20121.scala @@ -0,0 +1,13 @@ +sealed trait T_A[A, B] +type X = T_A[Byte, Byte] + +case class CC_B[A](a: A) extends T_A[A, X] + +val v_a: T_A[X, X] = CC_B(null) +val v_b = v_a match + case CC_B(_) => 0 // warn: unreachable + case _ => 1 + // for CC_B[A] to match T_A[X, X] + // A := X + // so require X, aka T_A[Byte, Byte] + // which isn't instantiable, outside of null diff --git a/tests/warn/i20122.scala b/tests/warn/i20122.scala new file mode 100644 index 000000000000..50da42a5926c --- /dev/null +++ b/tests/warn/i20122.scala @@ -0,0 +1,17 @@ +sealed trait T_B[C, D] + +case class CC_A() +case class CC_B[A, C](a: A) extends T_B[C, CC_A] +case class CC_C[C, D](a: T_B[C, D]) extends T_B[Int, CC_A] +case class CC_E(a: CC_C[Char, Byte]) + +val v_a: T_B[Int, CC_A] = CC_B(CC_E(CC_C(null))) +val v_b = v_a match + case CC_B(CC_E(CC_C(_))) => 0 // warn: unreachable + case _ => 1 + // for CC_B[A, C] to match T_B[C, CC_A] + // C <: Int, ok + // A <: CC_E, ok + // but you need a CC_C[Char, Byte] + // which requires a T_B[Char, Byte] + // which isn't instantiable, outside of null diff --git a/tests/warn/i20123.scala b/tests/warn/i20123.scala new file mode 100644 index 000000000000..32de903210b2 --- /dev/null +++ b/tests/warn/i20123.scala @@ -0,0 +1,16 @@ +sealed trait T_A[A, B] +sealed trait T_B[C] + +case class CC_D[A, C]() extends T_A[A, C] +case class CC_E() extends T_B[Nothing] +case class CC_G[A, C](c: C) extends T_A[A, C] + +val v_a: T_A[Boolean, T_B[Boolean]] = CC_G(null) +val v_b = v_a match { + case CC_D() => 0 + case CC_G(_) => 1 // warn: unreachable + // for CC_G[A, C] to match T_A[Boolean, T_B[Boolean]] + // A := Boolean, which is ok + // C := T_B[Boolean], + // which isn't instantiable, outside of null +} diff --git a/tests/warn/t2755.scala b/tests/warn/t2755.scala index 1aeb06d237d1..b51bd6aa7470 100644 --- a/tests/warn/t2755.scala +++ b/tests/warn/t2755.scala @@ -19,7 +19,7 @@ object Test { case x: Array[String] => x.size case x: Array[AnyRef] => 5 case x: Array[?] => 6 - case _ => 7 // warn: only null is matched + case _ => 7 } def f3[T](a: Array[T]) = a match { case x: Array[Int] => x(0) @@ -28,7 +28,7 @@ object Test { case x: Array[String] => x.size case x: Array[AnyRef] => 5 case x: Array[?] => 6 - case _ => 7 // warn: only null is matched + case _ => 7 }