From b720fe26714054723f9129d6f9d98ad664057499 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Sun, 10 Dec 2017 18:13:14 +0100 Subject: [PATCH 01/10] fix #3645: handle type alias in child instantiation --- .../tools/dotc/transform/patmat/Space.scala | 4 +-- tests/patmat/3543.check | 1 + tests/patmat/i3645.check | 0 tests/patmat/i3645.scala | 26 +++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/patmat/3543.check create mode 100644 tests/patmat/i3645.check create mode 100644 tests/patmat/i3645.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 267deb1f75a7..2941d2315f75 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -613,8 +613,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // replace type parameter references with bounds val typeParamMap = new TypeMap { - def apply(t: Type): Type = t match { - case tp: TypeRef if tp.symbol.is(TypeParam) && tp.underlying.isInstanceOf[TypeBounds] => + def apply(t: Type): Type = t.dealias match { + case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala val exposed = if (variance == 0) newTypeVar(tp.underlying.bounds) diff --git a/tests/patmat/3543.check b/tests/patmat/3543.check new file mode 100644 index 000000000000..a338bef12dd1 --- /dev/null +++ b/tests/patmat/3543.check @@ -0,0 +1 @@ +20: Pattern Match Exhaustivity: KInt diff --git a/tests/patmat/i3645.check b/tests/patmat/i3645.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/patmat/i3645.scala b/tests/patmat/i3645.scala new file mode 100644 index 000000000000..e009a6de2310 --- /dev/null +++ b/tests/patmat/i3645.scala @@ -0,0 +1,26 @@ +object App { + def main(args: Array[String]): Unit = { + trait AgeT { + type T + def subst[F[_]](fa: F[Int]): F[T] + } + + type Age = Age.T + + val Age: AgeT = new AgeT { + type T = Int + def subst[F[_]](fa: F[Int]): F[T] = fa + } + + sealed abstract class K[A] + final case object KAge extends K[Age] + final case object KInt extends K[Int] + + val kint: K[Age] = Age.subst[K](KInt) + def get(k: K[Age]): String = k match { + case KAge => "Age" + } + + get(kint) + } +} From 20552e1b115c537135bd0a20c432727e867e656e Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Sun, 10 Dec 2017 18:56:33 +0100 Subject: [PATCH 02/10] fix error with check file --- tests/patmat/3543.check | 1 - tests/patmat/i3645.check | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tests/patmat/3543.check diff --git a/tests/patmat/3543.check b/tests/patmat/3543.check deleted file mode 100644 index a338bef12dd1..000000000000 --- a/tests/patmat/3543.check +++ /dev/null @@ -1 +0,0 @@ -20: Pattern Match Exhaustivity: KInt diff --git a/tests/patmat/i3645.check b/tests/patmat/i3645.check index e69de29bb2d1..a338bef12dd1 100644 --- a/tests/patmat/i3645.check +++ b/tests/patmat/i3645.check @@ -0,0 +1 @@ +20: Pattern Match Exhaustivity: KInt From f03eade7fe69ded63b1602511047d1a17623adbb Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Sun, 10 Dec 2017 23:52:00 +0100 Subject: [PATCH 03/10] handle abstract types in child's parent list --- .../tools/dotc/transform/patmat/Space.scala | 19 ++++++++++-- tests/patmat/3645b.check | 1 + tests/patmat/3645b.scala | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/patmat/3645b.check create mode 100644 tests/patmat/3645b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2941d2315f75..6d95918563a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -602,10 +602,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // map `ThisType` of `tp1` to a type variable // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant val thisTypeMap = new TypeMap { - def apply(t: Type): Type = t match { + def apply(t: Type): Type = t.dealias match { case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => if (tref.symbol.is(Module)) mapOver(tref) else newTypeVar(TypeBounds.upper(tp.underlying)) + case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => + // See tests/patmat/3645b.scala + val exposed = + if (variance == 0) newTypeVar(tp.underlying.bounds) + else if (variance == 1) mapOver(tp.underlying.hiBound) + else mapOver(tp.underlying.loBound) + + debug.println(s"$tp exposed to =====> $exposed") + exposed case _ => mapOver(t) } @@ -644,13 +653,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } val protoTp1 = thisTypeMap(tp1.appliedTo(tvars)) + // tests/patmat/3645b.scala + def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent => + (parent.argInfos.nonEmpty || parent.abstractTypeMembers.nonEmpty) && + instantiate(parent, tp2)(ctx.fresh.setNewTyperState()).exists + } + if (protoTp1 <:< tp2) { if (isFullyDefined(protoTp1, force)) protoTp1 else instUndetMap(protoTp1) } else { val protoTp2 = typeParamMap(tp2) - if (protoTp1 <:< protoTp2) { + if (protoTp1 <:< protoTp2 || parentQualify) { if (isFullyDefined(AndType(protoTp1, protoTp2), force)) protoTp1 else instUndetMap(protoTp1) } diff --git a/tests/patmat/3645b.check b/tests/patmat/3645b.check new file mode 100644 index 000000000000..3d4d48cd44aa --- /dev/null +++ b/tests/patmat/3645b.check @@ -0,0 +1 @@ +21: Pattern Match Exhaustivity: K3, K2 diff --git a/tests/patmat/3645b.scala b/tests/patmat/3645b.scala new file mode 100644 index 000000000000..1f1e146ea8a0 --- /dev/null +++ b/tests/patmat/3645b.scala @@ -0,0 +1,29 @@ +object App { + def main(args: Array[String]): Unit = { + trait FooT { + type T + def subst[F[_]](fa: F[T]): F[Int] + } + val Foo: FooT = new FooT { + type T = Int + + def subst[F[_]](fa: F[T]): F[Int] = fa + } + type Foo = Foo.T + type Bar = Foo + + sealed abstract class K[A] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + final case object K3 extends K[Bar] + + val foo: K[Int] = Foo.subst[K](K2) + def get(k: K[Int]): Unit = k match { + case K1 => () + // case K2 => () + // case K3 => () + } + + get(foo) + } +} \ No newline at end of file From 912882301ab4105683fe0cf8ec2becc108cbe253 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 00:41:45 +0100 Subject: [PATCH 04/10] add comment --- .../dotty/tools/dotc/transform/patmat/Space.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 6d95918563a3..945877691714 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -607,11 +607,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (tref.symbol.is(Module)) mapOver(tref) else newTypeVar(TypeBounds.upper(tp.underlying)) case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => - // See tests/patmat/3645b.scala + // Note that the logic for contra- and co-variance is reverse of `typeParamMap` + // This is because we are checking the possibility of `tp1 <:< tp2`, thus we should + // minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala val exposed = if (variance == 0) newTypeVar(tp.underlying.bounds) - else if (variance == 1) mapOver(tp.underlying.hiBound) - else mapOver(tp.underlying.loBound) + else if (variance == 1) mapOver(tp.underlying.loBound) + else mapOver(tp.underlying.hiBound) debug.println(s"$tp exposed to =====> $exposed") exposed @@ -653,7 +655,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } val protoTp1 = thisTypeMap(tp1.appliedTo(tvars)) - // tests/patmat/3645b.scala + // If parent contains a reference to an abstract type, then we should + // refine subtype checking to eliminate abstract types according to + // variance. As this logic is only needed in exhaustivity check, thus + // we manually patch subtyping check instead of changing TypeComparer. + // See tests/patmat/3645b.scala def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent => (parent.argInfos.nonEmpty || parent.abstractTypeMembers.nonEmpty) && instantiate(parent, tp2)(ctx.fresh.setNewTyperState()).exists From 9634f1be2c7afaa7508ee29bcaf727928f9f7fc2 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 01:30:59 +0100 Subject: [PATCH 05/10] avoid unnecessary recursion --- .../tools/dotc/transform/patmat/Space.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 945877691714..098a88e3f2ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -601,7 +601,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { // map `ThisType` of `tp1` to a type variable // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant - val thisTypeMap = new TypeMap { + def childTypeMap(implicit ctx: Context) = new TypeMap { def apply(t: Type): Type = t.dealias match { case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => if (tref.symbol.is(Module)) mapOver(tref) @@ -623,7 +623,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } // replace type parameter references with bounds - val typeParamMap = new TypeMap { + def parentTypeMap(implicit ctx: Context) = new TypeMap { def apply(t: Type): Type = t.dealias match { case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala @@ -640,7 +640,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala - val instUndetMap = new TypeMap { + def instUndetMap(implicit ctx: Context) = new TypeMap { def apply(t: Type): Type = t match { case tvar: TypeVar if !tvar.isInstantiated => WildcardType(tvar.origin.underlying.bounds) case _ => mapOver(t) @@ -653,7 +653,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { ) val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } - val protoTp1 = thisTypeMap(tp1.appliedTo(tvars)) + val protoTp1 = childTypeMap.apply(tp1.appliedTo(tvars)) // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to @@ -661,19 +661,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // we manually patch subtyping check instead of changing TypeComparer. // See tests/patmat/3645b.scala def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent => - (parent.argInfos.nonEmpty || parent.abstractTypeMembers.nonEmpty) && - instantiate(parent, tp2)(ctx.fresh.setNewTyperState()).exists + implicit val ictx = ctx.fresh.setNewTyperState() + parent.argInfos.nonEmpty && childTypeMap.apply(parent) <:< parentTypeMap.apply(tp2) } if (protoTp1 <:< tp2) { if (isFullyDefined(protoTp1, force)) protoTp1 - else instUndetMap(protoTp1) + else instUndetMap.apply(protoTp1) } else { - val protoTp2 = typeParamMap(tp2) + val protoTp2 = parentTypeMap.apply(tp2) if (protoTp1 <:< protoTp2 || parentQualify) { if (isFullyDefined(AndType(protoTp1, protoTp2), force)) protoTp1 - else instUndetMap(protoTp1) + else instUndetMap.apply(protoTp1) } else { debug.println(s"$protoTp1 <:< $protoTp2 = false") From fecb6d0022f30cf3c1b3b39f976e9c92bbd4d4f8 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 01:45:28 +0100 Subject: [PATCH 06/10] add more tests for variance handling --- tests/patmat/{3645b.check => i3645b.check} | 0 tests/patmat/{3645b.scala => i3645b.scala} | 0 tests/patmat/i3645c.check | 1 + tests/patmat/i3645c.scala | 29 ++++++++++++++++++++++ tests/patmat/i3645d.check | 1 + tests/patmat/i3645d.scala | 29 ++++++++++++++++++++++ 6 files changed, 60 insertions(+) rename tests/patmat/{3645b.check => i3645b.check} (100%) rename tests/patmat/{3645b.scala => i3645b.scala} (100%) create mode 100644 tests/patmat/i3645c.check create mode 100644 tests/patmat/i3645c.scala create mode 100644 tests/patmat/i3645d.check create mode 100644 tests/patmat/i3645d.scala diff --git a/tests/patmat/3645b.check b/tests/patmat/i3645b.check similarity index 100% rename from tests/patmat/3645b.check rename to tests/patmat/i3645b.check diff --git a/tests/patmat/3645b.scala b/tests/patmat/i3645b.scala similarity index 100% rename from tests/patmat/3645b.scala rename to tests/patmat/i3645b.scala diff --git a/tests/patmat/i3645c.check b/tests/patmat/i3645c.check new file mode 100644 index 000000000000..3d4d48cd44aa --- /dev/null +++ b/tests/patmat/i3645c.check @@ -0,0 +1 @@ +21: Pattern Match Exhaustivity: K3, K2 diff --git a/tests/patmat/i3645c.scala b/tests/patmat/i3645c.scala new file mode 100644 index 000000000000..4b3ba75989c8 --- /dev/null +++ b/tests/patmat/i3645c.scala @@ -0,0 +1,29 @@ +object App { + def main(args: Array[String]): Unit = { + trait FooT { + type T + def subst[F[_]](fa: F[T]): F[Int] + } + val Foo: FooT = new FooT { + type T = Int + + def subst[F[_]](fa: F[T]): F[Int] = fa + } + type Foo = Foo.T + type Bar = Foo + + sealed abstract class K[+A] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + final case object K3 extends K[Bar] + + val foo: K[Int] = Foo.subst[K](K2) + def get(k: K[Int]): Unit = k match { + case K1 => () + // case K2 => () + // case K3 => () + } + + get(foo) + } +} \ No newline at end of file diff --git a/tests/patmat/i3645d.check b/tests/patmat/i3645d.check new file mode 100644 index 000000000000..3d4d48cd44aa --- /dev/null +++ b/tests/patmat/i3645d.check @@ -0,0 +1 @@ +21: Pattern Match Exhaustivity: K3, K2 diff --git a/tests/patmat/i3645d.scala b/tests/patmat/i3645d.scala new file mode 100644 index 000000000000..3bbe29e20545 --- /dev/null +++ b/tests/patmat/i3645d.scala @@ -0,0 +1,29 @@ +object App { + def main(args: Array[String]): Unit = { + trait FooT { + type T + def subst[F[_]](fa: F[T]): F[Int] + } + val Foo: FooT = new FooT { + type T = Int + + def subst[F[_]](fa: F[T]): F[Int] = fa + } + type Foo = Foo.T + type Bar = Foo + + sealed abstract class K[-A] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + final case object K3 extends K[Bar] + + val foo: K[Int] = Foo.subst[K](K2) + def get(k: K[Int]): Unit = k match { + case K1 => () + // case K2 => () + // case K3 => () + } + + get(foo) + } +} \ No newline at end of file From 65d4dcc72ac7270d09e0e21d710276ba47b8b119 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 09:39:57 +0100 Subject: [PATCH 07/10] eliminate type reference in bounds for tvars in child instantiation --- .../tools/dotc/transform/patmat/Space.scala | 22 +++++++----- tests/patmat/i3645e.check | 1 + tests/patmat/i3645e.scala | 35 +++++++++++++++++++ 3 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 tests/patmat/i3645e.check create mode 100644 tests/patmat/i3645e.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 098a88e3f2ab..3ed550f318ba 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -604,17 +604,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def childTypeMap(implicit ctx: Context) = new TypeMap { def apply(t: Type): Type = t.dealias match { case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => - if (tref.symbol.is(Module)) mapOver(tref) + if (tref.symbol.is(Module)) this(tref) else newTypeVar(TypeBounds.upper(tp.underlying)) + case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => - // Note that the logic for contra- and co-variance is reverse of `typeParamMap` + // Note that the logic for contra- and co-variance is reverse of `parentTypeMap` // This is because we are checking the possibility of `tp1 <:< tp2`, thus we should // minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala + val lo = tp.underlying.loBound + val hi = tp.underlying.hiBound val exposed = - if (variance == 0) newTypeVar(tp.underlying.bounds) - else if (variance == 1) mapOver(tp.underlying.loBound) - else mapOver(tp.underlying.hiBound) - + if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi))) + else if (variance == 1) this(lo) + else this(hi) debug.println(s"$tp exposed to =====> $exposed") exposed case _ => @@ -627,10 +629,12 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def apply(t: Type): Type = t.dealias match { case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala + val lo = tp.underlying.loBound + val hi = tp.underlying.hiBound val exposed = - if (variance == 0) newTypeVar(tp.underlying.bounds) - else if (variance == 1) mapOver(tp.underlying.hiBound) - else mapOver(tp.underlying.loBound) + if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi))) + else if (variance == 1) this(hi) + else this(lo) debug.println(s"$tp exposed to =====> $exposed") exposed diff --git a/tests/patmat/i3645e.check b/tests/patmat/i3645e.check new file mode 100644 index 000000000000..14a7e9e29ab2 --- /dev/null +++ b/tests/patmat/i3645e.check @@ -0,0 +1 @@ +29: Pattern Match Exhaustivity: K1 diff --git a/tests/patmat/i3645e.scala b/tests/patmat/i3645e.scala new file mode 100644 index 000000000000..7c4b13416712 --- /dev/null +++ b/tests/patmat/i3645e.scala @@ -0,0 +1,35 @@ +object App { + def main(args: Array[String]): Unit = { + trait ModuleSig { + type Upper + + trait FooSig { + type Type <: Upper + def subst[F[_]](fa: F[Int]): F[Type] + } + + val Foo: FooSig + } + val Module: ModuleSig = new ModuleSig { + type Upper = Int + + val Foo: FooSig = new FooSig { + type Type = Int + def subst[F[_]](fa: F[Int]): F[Type] = fa + } + } + type Upper = Module.Upper + type Foo = Module.Foo.Type + + sealed abstract class K[F] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + + val kv: K[Foo] = Module.Foo.subst[K](K1) + def test(k: K[Foo]): Unit = k match { + case K2 => () + } + + test(kv) + } +} From 58066c7cb4795dcb10935c690b35b168f59a69fe Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 17:09:49 +0100 Subject: [PATCH 08/10] fix default case recursion in child/parentTypeMap --- .../tools/dotc/transform/patmat/Space.scala | 8 ++--- tests/patmat/i3645f.check | 1 + tests/patmat/i3645f.scala | 36 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 tests/patmat/i3645f.check create mode 100644 tests/patmat/i3645f.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 3ed550f318ba..83482dd7214c 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -619,8 +619,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { else this(hi) debug.println(s"$tp exposed to =====> $exposed") exposed - case _ => - mapOver(t) + case tp => + mapOver(tp) } } @@ -638,8 +638,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { debug.println(s"$tp exposed to =====> $exposed") exposed - case _ => - mapOver(t) + case tp => + mapOver(tp) } } diff --git a/tests/patmat/i3645f.check b/tests/patmat/i3645f.check new file mode 100644 index 000000000000..ee7113bc1140 --- /dev/null +++ b/tests/patmat/i3645f.check @@ -0,0 +1 @@ +30: Pattern Match Exhaustivity: K1 diff --git a/tests/patmat/i3645f.scala b/tests/patmat/i3645f.scala new file mode 100644 index 000000000000..04d31aceea0f --- /dev/null +++ b/tests/patmat/i3645f.scala @@ -0,0 +1,36 @@ +object App { + def main(args: Array[String]): Unit = { + trait ModuleSig { + type U2 + type U1 + + trait FooSig { + type Type = (U1 & U2) + def subst[F[_]](fa: F[Int]): F[Type] + } + + val Foo: FooSig + } + val Module: ModuleSig = new ModuleSig { + type U1 = Int + type U2 = Int + + val Foo: FooSig = new FooSig { + // type Type = Int + def subst[F[_]](fa: F[Int]): F[Type] = fa + } + } + type Foo = Module.Foo.Type + + sealed abstract class K[F] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + + val kv: K[Foo] = Module.Foo.subst[K](K1) + def test(k: K[Foo]): Unit = k match { + case K2 => () + } + + test(kv) + } +} From cd9273950ad346e0f9f356c80bd4414d4a3b8cce Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 11 Dec 2017 22:34:57 +0100 Subject: [PATCH 09/10] handle higher-kinded types in child/parentTypeMap --- .../tools/dotc/transform/patmat/Space.scala | 76 ++++++++++++------- tests/patmat/i3645g.check | 1 + tests/patmat/i3645g.scala | 35 +++++++++ 3 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 tests/patmat/i3645g.check create mode 100644 tests/patmat/i3645g.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 83482dd7214c..59698eee7723 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -599,50 +599,68 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { * */ def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { - // map `ThisType` of `tp1` to a type variable - // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant - def childTypeMap(implicit ctx: Context) = new TypeMap { - def apply(t: Type): Type = t.dealias match { - case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => - if (tref.symbol.is(Module)) this(tref) - else newTypeVar(TypeBounds.upper(tp.underlying)) + // expose abstract type references to their bounds or tvars according to variance + abstract class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap { + def expose(tp: TypeRef): Type = { + val lo = this(tp.info.loBound) + val hi = this(tp.info.hiBound) + val exposed = + if (variance == 0) + newTypeVar(TypeBounds(lo, hi)) + else if (variance == 1) + if (maximize) hi else lo + else + if (maximize) lo else hi + + debug.println(s"$tp exposed to =====> $exposed") + exposed + } + override def mapOver(tp: Type): Type = tp match { case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => - // Note that the logic for contra- and co-variance is reverse of `parentTypeMap` - // This is because we are checking the possibility of `tp1 <:< tp2`, thus we should - // minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala - val lo = tp.underlying.loBound - val hi = tp.underlying.hiBound + // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala + expose(tp) + + case AppliedType(tycon: TypeRef, args) if tycon.underlying.isInstanceOf[TypeBounds] => + val args2 = args.map(this) + val lo = this(tycon.info.loBound).applyIfParameterized(args2) + val hi = this(tycon.info.hiBound).applyIfParameterized(args2) val exposed = - if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi))) - else if (variance == 1) this(lo) - else this(hi) + if (variance == 0) + newTypeVar(TypeBounds(lo, hi)) + else if (variance == 1) + if (maximize) hi else lo + else + if (maximize) lo else hi + debug.println(s"$tp exposed to =====> $exposed") exposed - case tp => - mapOver(tp) + + case _ => + super.mapOver(tp) } } - // replace type parameter references with bounds - def parentTypeMap(implicit ctx: Context) = new TypeMap { + // We are checking the possibility of `tp1 <:< tp2`, thus we should + // minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala + def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) { def apply(t: Type): Type = t.dealias match { - case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => - // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala - val lo = tp.underlying.loBound - val hi = tp.underlying.hiBound - val exposed = - if (variance == 0) newTypeVar(TypeBounds(this(lo), this(hi))) - else if (variance == 1) this(hi) - else this(lo) + // map `ThisType` of `tp1` to a type variable + // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant + case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => + if (tref.symbol.is(Module)) this(tref) + else newTypeVar(TypeBounds.upper(tp.underlying)) - debug.println(s"$tp exposed to =====> $exposed") - exposed case tp => mapOver(tp) } } + // replace type parameter references with bounds + def parentTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true) { + def apply(tp: Type): Type = mapOver(tp.dealias) + } + // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala def instUndetMap(implicit ctx: Context) = new TypeMap { def apply(t: Type): Type = t match { diff --git a/tests/patmat/i3645g.check b/tests/patmat/i3645g.check new file mode 100644 index 000000000000..14a7e9e29ab2 --- /dev/null +++ b/tests/patmat/i3645g.check @@ -0,0 +1 @@ +29: Pattern Match Exhaustivity: K1 diff --git a/tests/patmat/i3645g.scala b/tests/patmat/i3645g.scala new file mode 100644 index 000000000000..f1c85b5d9e8c --- /dev/null +++ b/tests/patmat/i3645g.scala @@ -0,0 +1,35 @@ +object App { + def main(args: Array[String]): Unit = { + trait ModuleSig { + type F[_] + type U + + trait FooSig { + type Type = F[U] + def subst[F[_]](fa: F[Int]): F[Type] + } + + val Foo: FooSig + } + val Module: ModuleSig = new ModuleSig { + type F[A] = Int + + val Foo: FooSig = new FooSig { + // type Type = Int + def subst[F[_]](fa: F[Int]): F[Type] = fa + } + } + type Foo = Module.Foo.Type + + sealed abstract class K[F] + final case object K1 extends K[Int] + final case object K2 extends K[Foo] + + val kv: K[Foo] = Module.Foo.subst[K](K1) + def test(k: K[Foo]): Unit = k match { + case K2 => () + } + + test(kv) + } +} From cf594d47fd91e48ec0be5433fd8dbddccbfeddf2 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 17 Jan 2018 17:50:19 +0100 Subject: [PATCH 10/10] Typos --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 59698eee7723..663a5c37cf4a 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -642,11 +642,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } // We are checking the possibility of `tp1 <:< tp2`, thus we should - // minimize `tp1` while maximize `tp2`. See tests/patmat/3645b.scala + // minimize `tp1` while maximizing `tp2`. See tests/patmat/3645b.scala def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) { def apply(t: Type): Type = t.dealias match { // map `ThisType` of `tp1` to a type variable - // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant + // precondition: `tp1` should have the same shape as `path.Child`, thus `ThisType` is always covariant case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => if (tref.symbol.is(Module)) this(tref) else newTypeVar(TypeBounds.upper(tp.underlying)) @@ -679,7 +679,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to - // variance. As this logic is only needed in exhaustivity check, thus + // variance. As this logic is only needed in exhaustivity check, // we manually patch subtyping check instead of changing TypeComparer. // See tests/patmat/3645b.scala def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>