@@ -72,7 +72,7 @@ case class Typ(tp: Type, decomposed: Boolean = true) extends Space
7272case class Prod (tp : Type , unappTp : TermRef , params : List [Space ]) extends Space
7373
7474/** Union of spaces */
75- case class Or (spaces : List [Space ]) extends Space
75+ case class Or (spaces : Seq [Space ]) extends Space
7676
7777/** abstract space logic */
7878trait SpaceLogic {
@@ -113,45 +113,37 @@ trait SpaceLogic {
113113 /** Display space in string format */
114114 def show (sp : Space ): String
115115
116- /** Simplify space using the laws, there's no nested union after simplify
117- *
118- * @param aggressive if true and OR space has less than 5 components, `simplify` will
119- * collapse `sp1 | sp2` to `sp1` if `sp2` is a subspace of `sp1`.
120- *
121- * This reduces noise in counterexamples.
122- */
123- def simplify (space : Space , aggressive : Boolean = false )(using Context ): Space = trace(s " simplify ${show(space)}, aggressive = $aggressive --> " , debug, x => show(x.asInstanceOf [Space ]))(space match {
116+ /** Simplify space using the laws, there's no nested union after simplify */
117+ def simplify (space : Space )(using Context ): Space = trace(s " simplify ${show(space)} --> " , debug, x => show(x.asInstanceOf [Space ]))(space match {
124118 case Prod (tp, fun, spaces) =>
125119 val sp = Prod (tp, fun, spaces.map(simplify(_)))
126120 if (sp.params.contains(Empty )) Empty
127121 else if (canDecompose(tp) && decompose(tp).isEmpty) Empty
128122 else sp
129123 case Or (spaces) =>
130- val buf = new mutable.ListBuffer [Space ]
131- def include (s : Space ) = if s != Empty then buf += s
132- for space <- spaces do
133- simplify(space) match
134- case Or (ss) => ss.foreach(include)
135- case s => include(s)
136- val set = buf.toList
137-
138- if (set.isEmpty) Empty
139- else if (set.size == 1 ) set.toList(0 )
140- else if (aggressive && spaces.size < 5 ) {
141- val res = set.map(sp => (sp, set.filter(_ ne sp))).find {
142- case (sp, sps) =>
143- isSubspace(sp, Or (sps))
144- }
145- if (res.isEmpty) Or (set)
146- else simplify(Or (res.get._2), aggressive)
147- }
148- else Or (set)
124+ val spaces2 = spaces.map(simplify(_)).filter(_ != Empty )
125+ if spaces2.isEmpty then Empty
126+ else Or (spaces2)
149127 case Typ (tp, _) =>
150128 if (canDecompose(tp) && decompose(tp).isEmpty) Empty
151129 else space
152130 case _ => space
153131 })
154132
133+ /** Remove a space if it's a subspace of remaining spaces
134+ *
135+ * Note: `dedup` will return the same result if the sequence >= 10
136+ */
137+ def dedup (spaces : Seq [Space ])(using Context ): Seq [Space ] =
138+ if (spaces.lengthCompare(1 ) <= 0 || spaces.lengthCompare(10 ) >= 0 ) spaces
139+ else {
140+ val res = spaces.map(sp => (sp, spaces.filter(_ ne sp))).find {
141+ case (sp, sps) => isSubspace(sp, Or (LazyList (sps : _* )))
142+ }
143+ if (res.isEmpty) spaces
144+ else res.get._2
145+ }
146+
155147 /** Flatten space to get rid of `Or` for pretty print */
156148 def flatten (space : Space )(using Context ): Seq [Space ] = space match {
157149 case Prod (tp, fun, spaces) =>
@@ -205,8 +197,8 @@ trait SpaceLogic {
205197
206198 (a, b) match {
207199 case (Empty , _) | (_, Empty ) => Empty
208- case (_, Or (ss)) => Or (ss.map(intersect(a, _)).filterConserve (_ ne Empty ))
209- case (Or (ss), _) => Or (ss.map(intersect(_, b)).filterConserve (_ ne Empty ))
200+ case (_, Or (ss)) => Or (ss.map(intersect(a, _)).filter (_ ne Empty ))
201+ case (Or (ss), _) => Or (ss.map(intersect(_, b)).filter (_ ne Empty ))
210202 case (Typ (tp1, _), Typ (tp2, _)) =>
211203 if (isSubType(tp1, tp2)) a
212204 else if (isSubType(tp2, tp1)) b
@@ -263,8 +255,6 @@ trait SpaceLogic {
263255 Empty
264256 else if (canDecompose(tp2))
265257 tryDecompose2(tp2)
266- else if (isSubType(tp2, tp1) && covers(fun, tp2))
267- minus(a, Prod (tp1, fun, signature(fun, tp1, ss.length).map(Typ (_, false ))))
268258 else
269259 a
270260 case (Prod (tp1, fun1, ss1), Prod (tp2, fun2, ss2)) =>
@@ -282,7 +272,7 @@ trait SpaceLogic {
282272 else if cache.forall(sub => isSubspace(sub, Empty )) then Empty
283273 else
284274 // `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)`
285- Or (range.map { i => Prod (tp1, fun1, ss1.updated(i, sub(i))) })
275+ Or (LazyList ( range : _* ) .map { i => Prod (tp1, fun1, ss1.updated(i, sub(i))) })
286276 }
287277 }
288278}
@@ -601,7 +591,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
601591 tp.dealias match {
602592 case AndType (tp1, tp2) =>
603593 intersect(Typ (tp1, false ), Typ (tp2, false )) match {
604- case Or (spaces) => spaces
594+ case Or (spaces) => spaces.toList
605595 case Empty => Nil
606596 case space => List (space)
607597 }
@@ -842,14 +832,15 @@ class SpaceEngine(using Context) extends SpaceLogic {
842832 val checkGADTSAT = shouldCheckExamples(selTyp)
843833
844834 val uncovered =
845- flatten(simplify(minus(project(selTyp), patternSpace), aggressive = true )).filter { s =>
835+ flatten(simplify(minus(project(selTyp), patternSpace))).filter( { s =>
846836 s != Empty && (! checkGADTSAT || satisfiable(s))
847- }
837+ })
848838
849839
850840 if uncovered.nonEmpty then
851841 val hasMore = uncovered.lengthCompare(6 ) > 0
852- report.warning(PatternMatchExhaustivity (show(uncovered.take(6 )), hasMore), sel.srcPos)
842+ val deduped = dedup(uncovered.take(6 ))
843+ report.warning(PatternMatchExhaustivity (show(deduped), hasMore), sel.srcPos)
853844 }
854845
855846 private def redundancyCheckable (sel : Tree ): Boolean =
@@ -908,11 +899,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
908899 // If explicit nulls are enabled, this check isn't needed because most of the cases
909900 // that would trigger it would also trigger unreachability warnings.
910901 if (! ctx.explicitNulls && i == cases.length - 1 && ! isNullLit(pat) ) {
911- simplify(minus(covered, prevs)) match {
912- case Typ (`constantNullType`, _) =>
902+ val spaces = flatten(simplify(minus(covered, prevs)))
903+ if spaces.lengthCompare(10 ) < 0 then
904+ dedup(spaces).toList match
905+ case Typ (`constantNullType`, _) :: Nil =>
913906 report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
914- case _ =>
915- }
907+ case s =>
908+ debug.println( " `_` matches = " + s)
916909 }
917910 }
918911 }
0 commit comments