@@ -13,6 +13,7 @@ import core.StdNames._
1313import core .NameOps ._
1414import core .Constants ._
1515import reporting .diagnostic .messages ._
16+ import config .Printers .{ exhaustivity => debug }
1617
1718/** Space logic for checking exhaustivity and unreachability of pattern matching
1819 *
@@ -28,8 +29,6 @@ import reporting.diagnostic.messages._
2829 * 3. A union of spaces `S1 | S2 | ...` is a space
2930 * 4. For a case class Kon(x1: T1, x2: T2, .., xn: Tn), if S1, S2, ..., Sn
3031 * are spaces, then `Kon(S1, S2, ..., Sn)` is a space.
31- * 5. A constant `Const(value, T)` is a point in space
32- * 6. A stable identifier `Var(sym, T)` is a space
3332 *
3433 * For the problem of exhaustivity check, its formulation in terms of space is as follows:
3534 *
@@ -67,15 +66,6 @@ case class Kon(tp: Type, params: List[Space]) extends Space
6766/** Union of spaces */
6867case class Or (spaces : List [Space ]) extends Space
6968
70- /** Point in space */
71- sealed trait Point extends Space
72-
73- /** Point representing variables(stable identifier) in patterns */
74- case class Var (sym : Symbol , tp : Type ) extends Point
75-
76- /** Point representing literal constants in patterns */
77- case class Const (value : Constant , tp : Type ) extends Point
78-
7969/** abstract space logic */
8070trait SpaceLogic {
8171 /** Is `tp1` a subtype of `tp2`? */
@@ -97,6 +87,9 @@ trait SpaceLogic {
9787 /** Get components of decomposable types */
9888 def decompose (tp : Type ): List [Space ]
9989
90+ /** Display space in string format */
91+ def show (sp : Space ): String
92+
10093 /** Simplify space using the laws, there's no nested union after simplify */
10194 def simplify (space : Space ): Space = space match {
10295 case Kon (tp, spaces) =>
@@ -137,7 +130,7 @@ trait SpaceLogic {
137130 def tryDecompose1 (tp : Type ) = canDecompose(tp) && isSubspace(Or (decompose(tp)), b)
138131 def tryDecompose2 (tp : Type ) = canDecompose(tp) && isSubspace(a, Or (decompose(tp)))
139132
140- (a, b) match {
133+ val res = (a, b) match {
141134 case (Empty , _) => true
142135 case (_, Empty ) => false
143136 case (Or (ss), _) => ss.forall(isSubspace(_, b))
@@ -157,25 +150,19 @@ trait SpaceLogic {
157150 simplify(minus(a, b)) == Empty
158151 case (Kon (tp1, ss1), Kon (tp2, ss2)) =>
159152 isEqualType(tp1, tp2) && ss1.zip(ss2).forall((isSubspace _).tupled)
160- case (Const (v1, _), Const (v2, _)) => v1 == v2
161- case (Const (_, tp1), Typ (tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2)
162- case (Const (_, _), Or (ss)) => ss.exists(isSubspace(a, _))
163- case (Const (_, _), _) => false
164- case (_, Const (_, _)) => false
165- case (Var (x, _), Var (y, _)) => x == y
166- case (Var (_, tp1), Typ (tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2)
167- case (Var (_, _), Or (ss)) => ss.exists(isSubspace(a, _))
168- case (Var (_, _), _) => false
169- case (_, Var (_, _)) => false
170153 }
154+
155+ debug.println(s " ${show(a)} < ${show(b)} = $res" )
156+
157+ res
171158 }
172159
173160 /** Intersection of two spaces */
174161 def intersect (a : Space , b : Space ): Space = {
175162 def tryDecompose1 (tp : Type ) = intersect(Or (decompose(tp)), b)
176163 def tryDecompose2 (tp : Type ) = intersect(a, Or (decompose(tp)))
177164
178- (a, b) match {
165+ val res = (a, b) match {
179166 case (Empty , _) | (_, Empty ) => Empty
180167 case (_, Or (ss)) => Or (ss.map(intersect(a, _)).filterConserve(_ ne Empty ))
181168 case (Or (ss), _) => Or (ss.map(intersect(_, b)).filterConserve(_ ne Empty ))
@@ -199,39 +186,19 @@ trait SpaceLogic {
199186 if (! isEqualType(tp1, tp2)) Empty
200187 else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty )) Empty
201188 else Kon (tp1, ss1.zip(ss2).map((intersect _).tupled))
202- case (Const (v1, _), Const (v2, _)) =>
203- if (v1 == v2) a else Empty
204- case (Const (_, tp1), Typ (tp2, _)) =>
205- if (isSubType(tp1, tp2)) a
206- else if (canDecompose(tp2)) tryDecompose2(tp2)
207- else Empty
208- case (Const (_, _), _) => Empty
209- case (Typ (tp1, _), Const (_, tp2)) =>
210- if (isSubType(tp2, tp1)) b
211- else if (canDecompose(tp1)) tryDecompose1(tp1)
212- else Empty
213- case (_, Const (_, _)) => Empty
214- case (Var (x, _), Var (y, _)) =>
215- if (x == y) a else Empty
216- case (Var (_, tp1), Typ (tp2, _)) =>
217- if (isSubType(tp1, tp2)) a
218- else if (canDecompose(tp2)) tryDecompose2(tp2)
219- else Empty
220- case (Var (_, _), _) => Empty
221- case (Typ (tp1, _), Var (_, tp2)) =>
222- if (isSubType(tp2, tp1)) b
223- else if (canDecompose(tp1)) tryDecompose1(tp1)
224- else Empty
225- case (_, Var (_, _)) => Empty
226189 }
190+
191+ debug.println(s " ${show(a)} & ${show(b)} = ${show(res)}" )
192+
193+ res
227194 }
228195
229196 /** The space of a not covered by b */
230197 def minus (a : Space , b : Space ): Space = {
231198 def tryDecompose1 (tp : Type ) = minus(Or (decompose(tp)), b)
232199 def tryDecompose2 (tp : Type ) = minus(a, Or (decompose(tp)))
233200
234- (a, b) match {
201+ val res = (a, b) match {
235202 case (Empty , _) => Empty
236203 case (_, Empty ) => a
237204 case (Typ (tp1, _), Typ (tp2, _)) =>
@@ -264,26 +231,11 @@ trait SpaceLogic {
264231 Or (ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1 ).map {
265232 case (ri, i) => Kon (tp1, ss1.updated(i, ri))
266233 })
267- case (Const (v1, _), Const (v2, _)) =>
268- if (v1 == v2) Empty else a
269- case (Const (_, tp1), Typ (tp2, _)) =>
270- if (isSubType(tp1, tp2)) Empty
271- else if (canDecompose(tp2)) tryDecompose2(tp2)
272- else a
273- case (Const (_, _), _) => a
274- case (Typ (tp1, _), Const (_, tp2)) => // Boolean & Java enum
275- if (canDecompose(tp1)) tryDecompose1(tp1)
276- else a
277- case (_, Const (_, _)) => a
278- case (Var (x, _), Var (y, _)) =>
279- if (x == y) Empty else a
280- case (Var (_, tp1), Typ (tp2, _)) =>
281- if (isSubType(tp1, tp2)) Empty
282- else if (canDecompose(tp2)) tryDecompose2(tp2)
283- else a
284- case (Var (_, _), _) => a
285- case (_, Var (_, _)) => a
286234 }
235+
236+ debug.println(s " ${show(a)} - ${show(b)} = ${show(res)}" )
237+
238+ res
287239 }
288240}
289241
@@ -297,19 +249,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
297249 * otherwise approximate extractors to Empty
298250 */
299251 def project (pat : Tree , roundUp : Boolean = true )(implicit ctx : Context ): Space = pat match {
300- case Literal (c) => Const (c, c.tpe)
301- case _ : BackquotedIdent => Var (pat.symbol, pat.tpe)
252+ case Literal (c) =>
253+ if (c.value.isInstanceOf [Symbol ])
254+ Typ (c.value.asInstanceOf [Symbol ].termRef, false )
255+ else
256+ Typ (ConstantType (c), false )
257+ case _ : BackquotedIdent => Typ (pat.tpe, false )
302258 case Ident (_) | Select (_, _) =>
303- pat.tpe.stripAnnots match {
304- case tp : TermRef =>
305- if (pat.symbol.is(Enum ))
306- Const (Constant (pat.symbol), tp)
307- else if (tp.underlyingIterator.exists(_.classSymbol.is(Module )))
308- Typ (tp.widenTermRefExpr.stripAnnots, false )
309- else
310- Var (pat.symbol, tp)
311- case tp => Typ (tp, false )
312- }
259+ Typ (pat.tpe.stripAnnots, false )
313260 case Alternative (trees) => Or (trees.map(project(_, roundUp)))
314261 case Bind (_, pat) => project(pat)
315262 case UnApply (_, _, pats) =>
@@ -345,7 +292,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
345292 /** Is `tp1` a subtype of `tp2`? */
346293 def isSubType (tp1 : Type , tp2 : Type ): Boolean = {
347294 // check SI-9657 and tests/patmat/gadt.scala
348- erase(tp1) <:< erase(tp2)
295+ val res = erase(tp1) <:< erase(tp2)
296+ debug.println(s " ${tp1.show} <:< ${tp2.show} = $res" )
297+ res
349298 }
350299
351300 def isEqualType (tp1 : Type , tp2 : Type ): Boolean = tp1 =:= tp2
@@ -373,29 +322,37 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
373322 }
374323 }
375324
325+ debug.println(s " candidates for ${tp.show} : [ ${children.map(_.show).mkString(" , " )}] " )
326+
376327 tp match {
377328 case OrType (tp1, tp2) => List (Typ (tp1, true ), Typ (tp2, true ))
378329 case _ if tp =:= ctx.definitions.BooleanType =>
379330 List (
380- Const ( Constant (true ), ctx.definitions. BooleanType ),
381- Const ( Constant (false ), ctx.definitions. BooleanType )
331+ Typ ( ConstantType ( Constant (true )), true ),
332+ Typ ( ConstantType ( Constant (false )), true )
382333 )
383334 case _ if tp.classSymbol.is(Enum ) =>
384- children.map(sym => Const ( Constant ( sym), tp ))
335+ children.map(sym => Typ ( sym.termRef, true ))
385336 case _ =>
386337 val parts = children.map { sym =>
387338 if (sym.is(ModuleClass ))
388- sym.asClass.classInfo.selfType
339+ refine(tp, sym.sourceModule.termRef)
340+ else if (sym.isTerm)
341+ refine(tp, sym.termRef)
389342 else if (sym.info.typeParams.length > 0 || tp.isInstanceOf [TypeRef ])
390343 refine(tp, sym.typeRef)
391344 else
392345 sym.typeRef
393346 } filter { tpe =>
394347 // Child class may not always be subtype of parent:
395348 // GADT & path-dependent types
396- tpe <:< expose(tp)
349+ val res = tpe <:< expose(tp)
350+ if (! res) debug.println(s " unqualified child ousted: ${tpe.show} !< ${tp.show}" )
351+ res
397352 }
398353
354+ debug.println(s " ${tp.show} decomposes to [ ${parts.map(_.show).mkString(" , " )}] " )
355+
399356 parts.map(Typ (_, true ))
400357 }
401358 }
@@ -409,20 +366,26 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
409366 * `path2`, then return `path1.B`.
410367 */
411368 def refine (tp1 : Type , tp2 : Type ): Type = (tp1, tp2) match {
412- case (tp1 : RefinedType , _) => tp1.wrapIfMember(refine(tp1.parent, tp2))
369+ case (tp1 : RefinedType , _ : TypeRef ) => tp1.wrapIfMember(refine(tp1.parent, tp2))
413370 case (tp1 : HKApply , _) => refine(tp1.superType, tp2)
414- case (TypeRef (ref1 : TypeProxy , _), tp2 @ TypeRef (ref2 : TypeProxy , name)) =>
415- if (ref1.underlying <:< ref2.underlying) TypeRef (ref1, name) else tp2
371+ case (TypeRef (ref1 : TypeProxy , _), tp2 @ TypeRef (ref2 : TypeProxy , _)) =>
372+ if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2
373+ case (TypeRef (ref1 : TypeProxy , _), tp2 @ TermRef (ref2 : TypeProxy , _)) =>
374+ if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2
416375 case _ => tp2
417376 }
418377
419378 /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
420379 def canDecompose (tp : Type ): Boolean = {
421- tp.classSymbol.is(allOf(Abstract , Sealed )) ||
380+ val res = tp.classSymbol.is(allOf(Abstract , Sealed )) ||
422381 tp.classSymbol.is(allOf(Trait , Sealed )) ||
423382 tp.isInstanceOf [OrType ] ||
424383 tp =:= ctx.definitions.BooleanType ||
425- tp.classSymbol.is(Enum )
384+ tp.classSymbol.is(allOf(Enum , Sealed )) // Enum value doesn't have Sealed flag
385+
386+ debug.println(s " decomposable: ${tp.show} = $res" )
387+
388+ res
426389 }
427390
428391 /** Show friendly type name with current scope in mind
@@ -474,14 +437,12 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
474437 def show (s : Space ): String = {
475438 def doShow (s : Space , mergeList : Boolean = false ): String = s match {
476439 case Empty => " "
477- case Const (v , _) => v .show
478- case Var (x , _) => x.show
440+ case Typ ( c : ConstantType , _) => c.value .show
441+ case Typ ( tp : TermRef , _) => tp.symbol.showName
479442 case Typ (tp, decomposed) =>
480443 val sym = tp.widen.classSymbol
481444
482- if (sym.is(ModuleClass ))
483- showType(tp)
484- else if (ctx.definitions.isTupleType(tp))
445+ if (ctx.definitions.isTupleType(tp))
485446 signature(tp).map(_ => " _" ).mkString(" (" , " , " , " )" )
486447 else if (sym.showFullName == " scala.collection.immutable.::" )
487448 if (mergeList) " _" else " List(_)"
@@ -523,7 +484,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
523484 }
524485
525486 val Match (sel, cases) = tree
526- isCheckable(sel.tpe.widen.deAnonymize.dealiasKeepAnnots)
487+ val res = isCheckable(sel.tpe.widen.deAnonymize.dealiasKeepAnnots)
488+ debug.println(s " checkable: ${sel.show} = $res" )
489+ res
527490 }
528491
529492
@@ -584,7 +547,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
584547 val selTyp = sel.tpe.widen.deAnonymize.dealias
585548
586549
587- val patternSpace = cases.map(x => project(x.pat)).reduce((a, b) => Or (List (a, b)))
550+ val patternSpace = cases.map({ x =>
551+ val space = project(x.pat)
552+ debug.println(s " ${x.pat.show} projects to ${show(space)}" )
553+ space
554+ }).reduce((a, b) => Or (List (a, b)))
588555 val uncovered = simplify(minus(Typ (selTyp, true ), patternSpace))
589556
590557 if (uncovered != Empty )
0 commit comments