Skip to content

Prune impossible types from exhaustivity checker #3574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ trait SpaceLogic {
else
// `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)`
Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map {
case (ri, i) => Prod(tp1, fun1, sym1, ss1.updated(i, ri), full)
})
case (ri, i) => Prod(tp1, fun1, sym1, ss1.updated(i, ri), full)
})

}

Expand Down Expand Up @@ -375,7 +375,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}

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

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

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

if (!resTp.exists) {
if (!resTp.exists || !inhabited(resTp)) {
debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")
NoType
}
Expand All @@ -571,6 +571,25 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
}

/** Can this type be inhabited by a value?
*
* Intersection between singleton types and other types is always empty
* the singleton type is not a subtype of the other type.
* See patmat/i3573.scala for an example.
*/
def inhabited(tpe: Type)(implicit ctx: Context): Boolean = {
val emptySingletonIntersection = new ExistsAccumulator({
case AndType(s: SingletonType, t) =>
!(s <:< t)
case AndType(t, s: SingletonType) =>
!(s <:< t)
case x =>
false
})

!emptySingletonIntersection(false, tpe)
}

/** Instantiate type `tp1` to be a subtype of `tp2`
*
* Return the instantiated type if type parameters and this type
Expand Down
38 changes: 38 additions & 0 deletions tests/patmat/i3574.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
object PathVariantDefns {
sealed trait AtomBase {
sealed trait Atom
case class Zero(value: String) extends Atom
}

trait Atom1 extends AtomBase {
case class One(value: String) extends Atom
}

trait Atom2 extends AtomBase {
case class Two(value: String) extends Atom
}

object Atoms01 extends AtomBase with Atom1 {
def f = {
val a: Atom = null
a match {
case Zero(x) => ???
case One(x) => ???
// match may not be exhaustive.
// It would fail on: PathVariantDefns.Atom2 & PathVariantDefns.Atoms01.type(PathVariantDefns.Atoms01).Two(_)
}
}
}

object Atoms02 extends AtomBase with Atom2 {
def f = {
val a: Atom = null
a match {
case Zero(x) => ???
case Two(x) => ???
// match may not be exhaustive.
// It would fail on: PathVariantDefns.Atom1 & PathVariantDefns.Atoms02.type(PathVariantDefns.Atoms02).One(_)
}
}
}
}