Skip to content

Commit dd0c9f2

Browse files
committed
Space: Use RHS of & when refining subtypes
This allows for a more precise type argument to be inferred, which leads to correct exhaustivity results.
1 parent ef653b6 commit dd0c9f2

File tree

4 files changed

+36
-7
lines changed

4 files changed

+36
-7
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ object TypeOps:
738738
* If the subtyping is true, the instantiated type `p.child[Vs]` is
739739
* returned. Otherwise, `NoType` is returned.
740740
*/
741-
def refineUsingParent(parent: Type, child: Symbol)(using Context): Type = {
741+
def refineUsingParent(parent: Type, child: Symbol, mixins: List[Type] = Nil)(using Context): Type = {
742742
// <local child> is a place holder from Scalac, it is hopeless to instantiate it.
743743
//
744744
// Quote from scalac (from nsc/symtab/classfile/Pickler.scala):
@@ -753,7 +753,7 @@ object TypeOps:
753753
val childTp = if (child.isTerm) child.termRef else child.typeRef
754754

755755
inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds.addMode(Mode.GadtConstraintInference)) {
756-
instantiateToSubType(childTp, parent).dealias
756+
instantiateToSubType(childTp, parent, mixins).dealias
757757
}
758758
}
759759

@@ -764,7 +764,7 @@ object TypeOps:
764764
*
765765
* Otherwise, return NoType.
766766
*/
767-
private def instantiateToSubType(tp1: NamedType, tp2: Type)(using Context): Type = {
767+
private def instantiateToSubType(tp1: NamedType, tp2: Type, mixins: List[Type])(using Context): Type = {
768768
// In order for a child type S to qualify as a valid subtype of the parent
769769
// T, we need to test whether it is possible S <: T.
770770
//
@@ -881,6 +881,7 @@ object TypeOps:
881881
}
882882

883883
def instantiate(): Type = {
884+
for tp <- mixins.reverseIterator do protoTp1 <:< tp
884885
maximizeType(protoTp1, NoSpan)
885886
wildApprox(protoTp1)
886887
}

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,11 @@ class SpaceEngine(using Context) extends SpaceLogic {
597597
}
598598

599599
/** Decompose a type into subspaces -- assume the type can be decomposed */
600-
def decompose(tp: Type): List[Typ] =
601-
tp.dealias match {
600+
def decompose(tp: Type): List[Typ] = trace(i"decompose($tp)", debug, show(_: Seq[Space])) {
601+
def rec(tp: Type, mixins: List[Type]): List[Typ] = tp.dealias match {
602602
case AndType(tp1, tp2) =>
603603
def decomposeComponent(tpA: Type, tpB: Type): List[Typ] =
604-
decompose(tpA).flatMap {
604+
rec(tpA, tpB :: mixins).flatMap {
605605
case Typ(tp, _) =>
606606
if tp <:< tpB then
607607
Typ(tp, decomposed = true) :: Nil
@@ -642,7 +642,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
642642

643643
val parts = children.map { sym =>
644644
val sym1 = if (sym.is(ModuleClass)) sym.sourceModule else sym
645-
val refined = TypeOps.refineUsingParent(tp, sym1)
645+
val refined = TypeOps.refineUsingParent(tp, sym1, mixins)
646646

647647
debug.println(sym1.show + " refined to " + refined.show)
648648

@@ -663,6 +663,8 @@ class SpaceEngine(using Context) extends SpaceLogic {
663663

664664
parts.map(Typ(_, true))
665665
}
666+
rec(tp, Nil)
667+
}
666668

667669
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
668670
def canDecompose(tp: Type): Boolean =

tests/pos/i16539.min.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// scalac: -Werror
2+
sealed trait Tag[A]
3+
4+
sealed trait Foo
5+
case class Bar[A](fn: A => Unit) extends Foo, Tag[A]
6+
7+
class Test:
8+
def pmat[A](sel: Foo & Tag[A]): Unit = sel match
9+
case Bar(_) =>

tests/pos/i16539.scala

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// scalac: -Werror
2+
sealed trait Tag[A]
3+
4+
enum Hidden:
5+
case Reveal[A](
6+
init: A,
7+
reduce: (A, A) => A
8+
) extends Hidden with Tag[A]
9+
10+
trait Handle[C]:
11+
def apply[A](c: C & Tag[A]): A
12+
13+
val x = new Handle[Hidden] {
14+
def apply[A](c: Hidden & Tag[A]): A = c match {
15+
case Hidden.Reveal(x, _) => x
16+
}
17+
}

0 commit comments

Comments
 (0)