Skip to content

Commit cfbcb60

Browse files
committed
handle higher-kinded types in child/parentTypeMap
1 parent bdc8905 commit cfbcb60

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
@@ -580,50 +580,68 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
580580
*
581581
*/
582582
def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
583-
// map `ThisType` of `tp1` to a type variable
584-
// precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
585-
def childTypeMap(implicit ctx: Context) = new TypeMap {
586-
def apply(t: Type): Type = t.dealias match {
587-
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
588-
if (tref.symbol.is(Module)) this(tref)
589-
else newTypeVar(TypeBounds.upper(tp.underlying))
583+
// expose abstract type references to their bounds or tvars according to variance
584+
abstract class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap {
585+
def expose(tp: TypeRef): Type = {
586+
val lo = this(tp.info.loBound)
587+
val hi = this(tp.info.hiBound)
588+
val exposed =
589+
if (variance == 0)
590+
newTypeVar(TypeBounds(lo, hi))
591+
else if (variance == 1)
592+
if (maximize) hi else lo
593+
else
594+
if (maximize) lo else hi
595+
596+
debug.println(s"$tp exposed to =====> $exposed")
597+
exposed
598+
}
590599

600+
override def mapOver(tp: Type): Type = tp match {
591601
case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
592-
// Note that the logic for contra- and co-variance is reverse of `parentTypeMap`
593-
// This is because we are checking the possibility of `tp1 <:< tp2`, thus we should
594-
// minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala
595-
val lo = tp.underlying.loBound
596-
val hi = tp.underlying.hiBound
602+
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
603+
expose(tp)
604+
605+
case AppliedType(tycon: TypeRef, args) if tycon.underlying.isInstanceOf[TypeBounds] =>
606+
val args2 = args.map(this)
607+
val lo = this(tycon.info.loBound).applyIfParameterized(args2)
608+
val hi = this(tycon.info.hiBound).applyIfParameterized(args2)
597609
val exposed =
598-
if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi)))
599-
else if (variance == 1) this(lo)
600-
else this(hi)
610+
if (variance == 0)
611+
newTypeVar(TypeBounds(lo, hi))
612+
else if (variance == 1)
613+
if (maximize) hi else lo
614+
else
615+
if (maximize) lo else hi
616+
601617
debug.println(s"$tp exposed to =====> $exposed")
602618
exposed
603-
case tp =>
604-
mapOver(tp)
619+
620+
case _ =>
621+
super.mapOver(tp)
605622
}
606623
}
607624

608-
// replace type parameter references with bounds
609-
def parentTypeMap(implicit ctx: Context) = new TypeMap {
625+
// We are checking the possibility of `tp1 <:< tp2`, thus we should
626+
// minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala
627+
def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) {
610628
def apply(t: Type): Type = t.dealias match {
611-
case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
612-
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
613-
val lo = tp.underlying.loBound
614-
val hi = tp.underlying.hiBound
615-
val exposed =
616-
if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi)))
617-
else if (variance == 1) this(hi)
618-
else this(lo)
629+
// map `ThisType` of `tp1` to a type variable
630+
// precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
631+
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
632+
if (tref.symbol.is(Module)) this(tref)
633+
else newTypeVar(TypeBounds.upper(tp.underlying))
619634

620-
debug.println(s"$tp exposed to =====> $exposed")
621-
exposed
622635
case tp =>
623636
mapOver(tp)
624637
}
625638
}
626639

640+
// replace type parameter references with bounds
641+
def parentTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true) {
642+
def apply(tp: Type): Type = mapOver(tp.dealias)
643+
}
644+
627645
// replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
628646
def instUndetMap(implicit ctx: Context) = new TypeMap {
629647
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)