Skip to content

Commit 5b6cb1a

Browse files
Replace Space.inhabited by typeComparer.intersecting
This commit also updates pattern matching exhaustivity tests for the new type inhavitation decision procedure. The diffs in t6420 and t7466 comes from a bug a the previous algorithm that would conclue that given `val a: Int`, `1` and `a.type` are non- intersecting because they are both singleton types. However, we can only get to that conclusion when both types are ConstantTypes. The diff in andtype-opentype-interaction come from the fact that the new decomposition of sealed types is more powerful than the previous one. For instance, we can now rule out `SealedClass & OpenTrait` as both types are provably non intersecting. i3574.scala is pending for now due to the removal of inhabitation check of childrens, it will be enabled back in a subsequent commit.
1 parent bfecaae commit 5b6cb1a

File tree

5 files changed

+12
-108
lines changed

5 files changed

+12
-108
lines changed

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

Lines changed: 4 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
298298
// Since projections of types don't include null, intersection with null is empty.
299299
return Empty
300300
}
301-
val and = AndType(tp1, tp2)
302-
// Then, no leaf of the and-type tree `and` is a subtype of `and`.
303-
val res = inhabited(and)
301+
val res = ctx.typeComparer.intersecting(tp1, tp2)
304302

305-
debug.println(s"atomic intersection: ${and.show} = ${res}")
303+
debug.println(s"atomic intersection: ${AndType(tp1, tp2).show} = ${res}")
306304

307305
if (!res) Empty
308306
else if (tp1.isSingleton) Typ(tp1, true)
309307
else if (tp2.isSingleton) Typ(tp2, true)
310-
else Typ(and, true)
308+
else Typ(AndType(tp1, tp2), true)
311309
}
312310

313311
/** Whether the extractor is irrefutable */
@@ -516,101 +514,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
516514

517515
val childTp = if (child.isTerm) child.termRef else child.typeRef
518516

519-
val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState())
520-
521-
if (!resTp.exists || !inhabited(resTp)) {
522-
debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")
523-
NoType
524-
}
525-
else {
526-
debug.println(s"$child instantiated ------> $resTp")
527-
resTp.dealias
528-
}
529-
}
530-
531-
/** Can this type be inhabited by a value?
532-
*
533-
* Check is based on the following facts:
534-
*
535-
* - single inheritance of classes
536-
* - final class cannot be extended
537-
* - intersection of a singleton type with another irrelevant type (patmat/i3574.scala)
538-
*
539-
*/
540-
def inhabited(tp: Type)(implicit ctx: Context): Boolean = {
541-
// convert top-level type shape into "conjunctive normal form"
542-
def cnf(tp: Type): Type = tp match {
543-
case AndType(OrType(l, r), tp) =>
544-
OrType(cnf(AndType(l, tp)), cnf(AndType(r, tp)))
545-
case AndType(tp, o: OrType) =>
546-
cnf(AndType(o, tp))
547-
case AndType(l, r) =>
548-
val l1 = cnf(l)
549-
val r1 = cnf(r)
550-
if (l1.ne(l) || r1.ne(r)) cnf(AndType(l1, r1))
551-
else AndType(l1, r1)
552-
case OrType(l, r) =>
553-
OrType(cnf(l), cnf(r))
554-
case tp @ RefinedType(OrType(tp1, tp2), _, _) =>
555-
OrType(
556-
cnf(tp.derivedRefinedType(tp1, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo)),
557-
cnf(tp.derivedRefinedType(tp2, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo))
558-
)
559-
case tp: RefinedType =>
560-
val parent1 = cnf(tp.parent)
561-
val tp1 = tp.derivedRefinedType(parent1, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo)
562-
563-
if (parent1.ne(tp.parent)) cnf(tp1) else tp1
564-
case tp: TypeAlias =>
565-
cnf(tp.alias)
566-
case _ =>
567-
tp
568-
}
569-
570-
def isSingleton(tp: Type): Boolean = tp.dealias match {
571-
case AndType(l, r) => isSingleton(l) || isSingleton(r)
572-
case OrType(l, r) => isSingleton(l) && isSingleton(r)
573-
case tp => tp.isSingleton
574-
}
575-
576-
def recur(tp: Type): Boolean = tp.dealias match {
577-
case AndType(tp1, tp2) =>
578-
recur(tp1) && recur(tp2) && {
579-
val bases1 = tp1.widenDealias.classSymbols
580-
val bases2 = tp2.widenDealias.classSymbols
581-
582-
debug.println(s"bases of ${tp1.show}: " + bases1)
583-
debug.println(s"bases of ${tp2.show}: " + bases2)
584-
debug.println(s"${tp1.show} <:< ${tp2.show} : " + (tp1 <:< tp2))
585-
debug.println(s"${tp2.show} <:< ${tp1.show} : " + (tp2 <:< tp1))
586-
587-
val noClassConflict =
588-
bases1.forall(sym1 => sym1.is(Trait) || bases2.forall(sym2 => sym2.is(Trait) || sym1.isSubClass(sym2))) ||
589-
bases1.forall(sym1 => sym1.is(Trait) || bases2.forall(sym2 => sym2.is(Trait) || sym2.isSubClass(sym1)))
590-
591-
debug.println(s"class conflict for ${tp.show}? " + !noClassConflict)
592-
593-
noClassConflict &&
594-
(!isSingleton(tp1) || tp1 <:< tp2) &&
595-
(!isSingleton(tp2) || tp2 <:< tp1) &&
596-
(!bases1.exists(_ is Final) || tp1 <:< maxTypeMap.apply(tp2)) &&
597-
(!bases2.exists(_ is Final) || tp2 <:< maxTypeMap.apply(tp1))
598-
}
599-
case OrType(tp1, tp2) =>
600-
recur(tp1) || recur(tp2)
601-
case tp: RefinedType =>
602-
recur(tp.parent)
603-
case tp: TypeRef =>
604-
recur(tp.prefix) && !(tp.classSymbol.is(AbstractFinal))
605-
case _ =>
606-
true
607-
}
608-
609-
val res = recur(cnf(tp))
610-
611-
debug.println(s"${tp.show} inhabited? " + res)
612-
613-
res
517+
instantiate(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
614518
}
615519

616520
/** expose abstract type references to their bounds or tvars according to variance */
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
23: Pattern Match Exhaustivity: _: Trait & OpenTrait, _: Clazz & OpenTrait, _: AbstractClass & OpenTrait, _: SealedTrait & OpenTrait, _: SealedClass & OpenTrait, _: SealedAbstractClass & OpenTrait
2-
27: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: AbstractClass & OpenTrait & OpenTrait2, _: SealedTrait & OpenTrait & OpenTrait2, _: SealedClass & OpenTrait & OpenTrait2, _: SealedAbstractClass & OpenTrait & OpenTrait2
3-
31: Pattern Match Exhaustivity: _: Trait & OpenClass, _: SealedTrait & OpenClass
4-
35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass, _: SealedTrait & OpenTrait & OpenClass
5-
43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass, _: SealedTrait & OpenAbstractClass
6-
47: Pattern Match Exhaustivity: _: Trait & OpenClass & (OpenTrait & OpenClassSubclass), _: SealedTrait & OpenClass & (OpenTrait & OpenClassSubclass)
1+
23: Pattern Match Exhaustivity: _: Trait & OpenTrait, _: Clazz & OpenTrait, _: AbstractClass & OpenTrait, _: SealedClass & OpenTrait
2+
27: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: AbstractClass & OpenTrait & OpenTrait2, _: SealedClass & OpenTrait & OpenTrait2
3+
31: Pattern Match Exhaustivity: _: Trait & OpenClass
4+
35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass
5+
43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass
6+
47: Pattern Match Exhaustivity: _: Trait & OpenClass & (OpenTrait & OpenClassSubclass)

tests/patmat/t6420.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
5: Pattern Match Exhaustivity: (_: List, List(true, _: _*)), (_: List, List(false, _: _*)), (_: List, Nil)
1+
5: Pattern Match Exhaustivity: (List(true, _: _*), List(true, _: _*)), (List(true, _: _*), List(false, _: _*)), (List(true, _: _*), Nil), (List(false, _: _*), List(true, _: _*)), (List(false, _: _*), List(false, _: _*)), (List(false, _: _*), Nil), (Nil, List(true, _: _*)), (Nil, List(false, _: _*)), (Nil, Nil), (_: List, List(true, _: _*)), (_: List, List(false, _: _*)), (_: List, Nil)

tests/patmat/t7466.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8: Pattern Match Exhaustivity: (_, true), (_, false)
1+
8: Pattern Match Exhaustivity: (true, true), (true, false), (false, true), (false, false), (_, true), (_, false)
File renamed without changes.

0 commit comments

Comments
 (0)