Skip to content

Commit e55c931

Browse files
Prune impossible types from exaustivity checker
1 parent 924234f commit e55c931

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
375375
}
376376

377377
/* Whether the extractor is irrefutable */
378-
def irrefutable(unapp: tpd.Tree): Boolean = {
378+
def irrefutable(unapp: Tree): Boolean = {
379379
// TODO: optionless patmat
380380
unapp.tpe.widen.finalResultType.isRef(scalaSomeClass) ||
381381
(unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case)) ||
@@ -557,11 +557,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
557557
def refine(parent: Type, child: Symbol): Type = {
558558
if (child.isTerm && child.is(Case, butNot = Module)) return child.termRef // enum vals always match
559559

560-
val childTp = if (child.isTerm) child.termRef else child.typeRef
560+
val childTp = if (child.isTerm) child.termRef else child.typeRef
561561

562562
val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState())
563563

564-
if (!resTp.exists) {
564+
if (!resTp.exists) {
565565
debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")
566566
NoType
567567
}
@@ -625,7 +625,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
625625
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
626626
val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))
627627

628-
if (protoTp1 <:< tp2) {
628+
val result = if (protoTp1 <:< tp2) {
629629
if (isFullyDefined(protoTp1, force)) protoTp1
630630
else instUndetMap(protoTp1)
631631
}
@@ -640,6 +640,24 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
640640
NoType
641641
}
642642
}
643+
644+
// The intersection between a singleton type s and another type t is
645+
// always empty when s is not a subtype of t. See patmat/i3573.scala
646+
val hasEmptyIntersections = new ExistsAccumulator({
647+
case AndType(s: SingletonType, t) =>
648+
!(s <:< t)
649+
case AndType(t, s: SingletonType) =>
650+
!(s <:< t)
651+
case x =>
652+
false
653+
})
654+
655+
if (hasEmptyIntersections(false, result)) {
656+
debug.println(s"hasEmptyIntersections($protoTp1) = true")
657+
NoType
658+
} else {
659+
result
660+
}
643661
}
644662

645663
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */

tests/patmat/i3574.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
object PathVariantDefns {
2+
sealed trait AtomBase {
3+
sealed trait Atom
4+
case class Zero(value: String) extends Atom
5+
}
6+
7+
trait Atom1 extends AtomBase {
8+
case class One(value: String) extends Atom
9+
}
10+
11+
trait Atom2 extends AtomBase {
12+
case class Two(value: String) extends Atom
13+
}
14+
15+
object Atoms01 extends AtomBase with Atom1 {
16+
def f = {
17+
val a: Atom = null
18+
a match {
19+
case Zero(x) => ???
20+
case One(x) => ???
21+
// match may not be exhaustive.
22+
// It would fail on: PathVariantDefns.Atom2 & PathVariantDefns.Atoms01.type(PathVariantDefns.Atoms01).Two(_)
23+
}
24+
}
25+
}
26+
27+
object Atoms02 extends AtomBase with Atom2 {
28+
def f = {
29+
val a: Atom = null
30+
a match {
31+
case Zero(x) => ???
32+
case Two(x) => ???
33+
// match may not be exhaustive.
34+
// It would fail on: PathVariantDefns.Atom1 & PathVariantDefns.Atoms02.type(PathVariantDefns.Atoms02).One(_)
35+
}
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)