Skip to content

Commit

Permalink
Fix #21914: Don't project nested wildcard patterns to nullable (#21934)
Browse files Browse the repository at this point in the history
Fix #21914

The wildcard patterns are projected to nullable #21850, which increases
the running time exponentially for complex patterns, due to the way we
simplify and minus spaces.

However, the current space analyse setup is only able to track nullable
value at top level, so we should not project nested wildcard patterns
(not at top level) to nullable.

The warnings in `tests/warn/i20121.scala`, `tests/warn/i20122.scala`,
and `tests/warn/i20123.scala` will become wrong again, but there is no
simple solution to fix them quickly.

I couldn't create a minimised test for #21914, but I have verified
locally the compile time becomes normal again with this fix.
  • Loading branch information
noti0na1 authored Nov 13, 2024
2 parents 6f48c39 + 0d5e014 commit bed0e86
Show file tree
Hide file tree
Showing 7 changed files with 12 additions and 14 deletions.
14 changes: 5 additions & 9 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -337,13 +337,6 @@ object SpaceEngine {
case pat: Ident if isBackquoted(pat) =>
Typ(pat.tpe, decomposed = false)

case Ident(nme.WILDCARD) =>
val tp = pat.tpe.stripAnnots.widenSkolem
val isNullable = tp.isInstanceOf[FlexibleType] || tp.classSymbol.isNullableClass
val tpSpace = Typ(erase(tp, isValue = true), decomposed = false)
if isNullable then Or(tpSpace :: nullSpace :: Nil)
else tpSpace

case Ident(_) | Select(_, _) =>
Typ(erase(pat.tpe.stripAnnots.widenSkolem, isValue = true), decomposed = false)

Expand Down Expand Up @@ -716,7 +709,6 @@ object SpaceEngine {
else NoType
}.filter(_.exists)
parts
case tp: FlexibleType => List(tp.underlying, ConstantType(Constant(null)))
case _ => ListOfNoType
end rec

Expand Down Expand Up @@ -939,11 +931,15 @@ object SpaceEngine {
then project(OrType(selTyp, ConstantType(Constant(null)), soft = false))
else project(selTyp)
var hadNullOnly = false
def projectPat(pat: Tree): Space =
// Project toplevel wildcard pattern to nullable
if isNullable && isWildcardArg(pat) then Or(project(pat) :: nullSpace :: Nil)
else project(pat)
@tailrec def recur(cases: List[CaseDef], prevs: List[Space], deferred: List[Tree]): Unit =
cases match
case Nil =>
case CaseDef(pat, guard, _) :: rest =>
val curr = trace(i"project($pat)")(project(pat))
val curr = trace(i"project($pat)")(projectPat(pat))
val covered = trace("covered")(simplify(intersect(curr, targetSpace)))
val prev = trace("prev")(simplify(Or(prevs)))
if prev == Empty && covered == Empty then // defer until a case is reachable
Expand Down
1 change: 1 addition & 0 deletions tests/patmat/i12530.check
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
6: Match case Unreachable
14: Match case Unreachable
2 changes: 1 addition & 1 deletion tests/patmat/null.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
6: Match case Unreachable
13: Pattern Match
18: Match case Unreachable
20: Pattern Match
21: Match case Unreachable
1 change: 1 addition & 0 deletions tests/patmat/null.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ class Test {
case Some(null) =>
case None =>
case y =>
case _ =>
}
}
4 changes: 2 additions & 2 deletions tests/warn/i20121.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ 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
case _ => 1 // warn: null only
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]
Expand Down
2 changes: 1 addition & 1 deletion tests/warn/i20122.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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
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
Expand Down
2 changes: 1 addition & 1 deletion tests/warn/i20123.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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
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],
Expand Down

0 comments on commit bed0e86

Please sign in to comment.