Skip to content

Commit bed0e86

Browse files
authored
Fix #21914: Don't project nested wildcard patterns to nullable (#21934)
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.
2 parents 6f48c39 + 0d5e014 commit bed0e86

File tree

7 files changed

+12
-14
lines changed

7 files changed

+12
-14
lines changed

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

+5-9
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,6 @@ object SpaceEngine {
337337
case pat: Ident if isBackquoted(pat) =>
338338
Typ(pat.tpe, decomposed = false)
339339

340-
case Ident(nme.WILDCARD) =>
341-
val tp = pat.tpe.stripAnnots.widenSkolem
342-
val isNullable = tp.isInstanceOf[FlexibleType] || tp.classSymbol.isNullableClass
343-
val tpSpace = Typ(erase(tp, isValue = true), decomposed = false)
344-
if isNullable then Or(tpSpace :: nullSpace :: Nil)
345-
else tpSpace
346-
347340
case Ident(_) | Select(_, _) =>
348341
Typ(erase(pat.tpe.stripAnnots.widenSkolem, isValue = true), decomposed = false)
349342

@@ -716,7 +709,6 @@ object SpaceEngine {
716709
else NoType
717710
}.filter(_.exists)
718711
parts
719-
case tp: FlexibleType => List(tp.underlying, ConstantType(Constant(null)))
720712
case _ => ListOfNoType
721713
end rec
722714

@@ -939,11 +931,15 @@ object SpaceEngine {
939931
then project(OrType(selTyp, ConstantType(Constant(null)), soft = false))
940932
else project(selTyp)
941933
var hadNullOnly = false
934+
def projectPat(pat: Tree): Space =
935+
// Project toplevel wildcard pattern to nullable
936+
if isNullable && isWildcardArg(pat) then Or(project(pat) :: nullSpace :: Nil)
937+
else project(pat)
942938
@tailrec def recur(cases: List[CaseDef], prevs: List[Space], deferred: List[Tree]): Unit =
943939
cases match
944940
case Nil =>
945941
case CaseDef(pat, guard, _) :: rest =>
946-
val curr = trace(i"project($pat)")(project(pat))
942+
val curr = trace(i"project($pat)")(projectPat(pat))
947943
val covered = trace("covered")(simplify(intersect(curr, targetSpace)))
948944
val prev = trace("prev")(simplify(Or(prevs)))
949945
if prev == Empty && covered == Empty then // defer until a case is reachable

tests/patmat/i12530.check

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
6: Match case Unreachable
2+
14: Match case Unreachable

tests/patmat/null.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
6: Match case Unreachable
22
13: Pattern Match
3-
18: Match case Unreachable
43
20: Pattern Match
4+
21: Match case Unreachable

tests/patmat/null.scala

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ class Test {
1818
case Some(null) =>
1919
case None =>
2020
case y =>
21+
case _ =>
2122
}
2223
}

tests/warn/i20121.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ case class CC_B[A](a: A) extends T_A[A, X]
55

66
val v_a: T_A[X, X] = CC_B(null)
77
val v_b = v_a match
8-
case CC_B(_) => 0
9-
case _ => 1 // warn: null only
8+
case CC_B(_) => 0 // warn: unreachable
9+
case _ => 1
1010
// for CC_B[A] to match T_A[X, X]
1111
// A := X
1212
// so require X, aka T_A[Byte, Byte]

tests/warn/i20122.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ case class CC_E(a: CC_C[Char, Byte])
77

88
val v_a: T_B[Int, CC_A] = CC_B(CC_E(CC_C(null)))
99
val v_b = v_a match
10-
case CC_B(CC_E(CC_C(_))) => 0
10+
case CC_B(CC_E(CC_C(_))) => 0 // warn: unreachable
1111
case _ => 1
1212
// for CC_B[A, C] to match T_B[C, CC_A]
1313
// C <: Int, ok

tests/warn/i20123.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ case class CC_G[A, C](c: C) extends T_A[A, C]
88
val v_a: T_A[Boolean, T_B[Boolean]] = CC_G(null)
99
val v_b = v_a match {
1010
case CC_D() => 0
11-
case CC_G(_) => 1
11+
case CC_G(_) => 1 // warn: unreachable
1212
// for CC_G[A, C] to match T_A[Boolean, T_B[Boolean]]
1313
// A := Boolean, which is ok
1414
// C := T_B[Boolean],

0 commit comments

Comments
 (0)