Skip to content

Commit cd92739

Browse files
committed
handle higher-kinded types in child/parentTypeMap
1 parent 58066c7 commit cd92739

File tree

3 files changed

+83
-29
lines changed

3 files changed

+83
-29
lines changed

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

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -599,50 +599,68 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
599599
*
600600
*/
601601
def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
602-
// map `ThisType` of `tp1` to a type variable
603-
// precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
604-
def childTypeMap(implicit ctx: Context) = new TypeMap {
605-
def apply(t: Type): Type = t.dealias match {
606-
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
607-
if (tref.symbol.is(Module)) this(tref)
608-
else newTypeVar(TypeBounds.upper(tp.underlying))
602+
// expose abstract type references to their bounds or tvars according to variance
603+
abstract class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap {
604+
def expose(tp: TypeRef): Type = {
605+
val lo = this(tp.info.loBound)
606+
val hi = this(tp.info.hiBound)
607+
val exposed =
608+
if (variance == 0)
609+
newTypeVar(TypeBounds(lo, hi))
610+
else if (variance == 1)
611+
if (maximize) hi else lo
612+
else
613+
if (maximize) lo else hi
614+
615+
debug.println(s"$tp exposed to =====> $exposed")
616+
exposed
617+
}
609618

619+
override def mapOver(tp: Type): Type = tp match {
610620
case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
611-
// Note that the logic for contra- and co-variance is reverse of `parentTypeMap`
612-
// This is because we are checking the possibility of `tp1 <:< tp2`, thus we should
613-
// minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala
614-
val lo = tp.underlying.loBound
615-
val hi = tp.underlying.hiBound
621+
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
622+
expose(tp)
623+
624+
case AppliedType(tycon: TypeRef, args) if tycon.underlying.isInstanceOf[TypeBounds] =>
625+
val args2 = args.map(this)
626+
val lo = this(tycon.info.loBound).applyIfParameterized(args2)
627+
val hi = this(tycon.info.hiBound).applyIfParameterized(args2)
616628
val exposed =
617-
if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi)))
618-
else if (variance == 1) this(lo)
619-
else this(hi)
629+
if (variance == 0)
630+
newTypeVar(TypeBounds(lo, hi))
631+
else if (variance == 1)
632+
if (maximize) hi else lo
633+
else
634+
if (maximize) lo else hi
635+
620636
debug.println(s"$tp exposed to =====> $exposed")
621637
exposed
622-
case tp =>
623-
mapOver(tp)
638+
639+
case _ =>
640+
super.mapOver(tp)
624641
}
625642
}
626643

627-
// replace type parameter references with bounds
628-
def parentTypeMap(implicit ctx: Context) = new TypeMap {
644+
// We are checking the possibility of `tp1 <:< tp2`, thus we should
645+
// minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala
646+
def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) {
629647
def apply(t: Type): Type = t.dealias match {
630-
case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
631-
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
632-
val lo = tp.underlying.loBound
633-
val hi = tp.underlying.hiBound
634-
val exposed =
635-
if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi)))
636-
else if (variance == 1) this(hi)
637-
else this(lo)
648+
// map `ThisType` of `tp1` to a type variable
649+
// precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
650+
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
651+
if (tref.symbol.is(Module)) this(tref)
652+
else newTypeVar(TypeBounds.upper(tp.underlying))
638653

639-
debug.println(s"$tp exposed to =====> $exposed")
640-
exposed
641654
case tp =>
642655
mapOver(tp)
643656
}
644657
}
645658

659+
// replace type parameter references with bounds
660+
def parentTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true) {
661+
def apply(tp: Type): Type = mapOver(tp.dealias)
662+
}
663+
646664
// replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
647665
def instUndetMap(implicit ctx: Context) = new TypeMap {
648666
def apply(t: Type): Type = t match {

tests/patmat/i3645g.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
29: Pattern Match Exhaustivity: K1

tests/patmat/i3645g.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
object App {
2+
def main(args: Array[String]): Unit = {
3+
trait ModuleSig {
4+
type F[_]
5+
type U
6+
7+
trait FooSig {
8+
type Type = F[U]
9+
def subst[F[_]](fa: F[Int]): F[Type]
10+
}
11+
12+
val Foo: FooSig
13+
}
14+
val Module: ModuleSig = new ModuleSig {
15+
type F[A] = Int
16+
17+
val Foo: FooSig = new FooSig {
18+
// type Type = Int
19+
def subst[F[_]](fa: F[Int]): F[Type] = fa
20+
}
21+
}
22+
type Foo = Module.Foo.Type
23+
24+
sealed abstract class K[F]
25+
final case object K1 extends K[Int]
26+
final case object K2 extends K[Foo]
27+
28+
val kv: K[Foo] = Module.Foo.subst[K](K1)
29+
def test(k: K[Foo]): Unit = k match {
30+
case K2 => ()
31+
}
32+
33+
test(kv)
34+
}
35+
}

0 commit comments

Comments
 (0)