Skip to content

Commit 7058f69

Browse files
authored
Merge pull request #3939 from dotty-staging/fix-3938
Fix #3938: adapt prefix to ease prefix inference
2 parents 0a05275 + 92a2e54 commit 7058f69

File tree

2 files changed

+62
-25
lines changed

2 files changed

+62
-25
lines changed

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

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
557557

558558
debug.println(s"bases of ${tp1.show}: " + bases1)
559559
debug.println(s"bases of ${tp2.show}: " + bases2)
560+
debug.println(s"${tp1.show} <:< ${tp2.show} : " + (tp1 <:< tp2))
561+
debug.println(s"${tp2.show} <:< ${tp1.show} : " + (tp2 <:< tp1))
560562

561563
val noClassConflict =
562564
bases1.forall(sym1 => sym1.is(Trait) || bases2.forall(sym2 => sym2.is(Trait) || sym1.isSubClass(sym2))) ||
@@ -595,9 +597,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
595597
* Otherwise, return NoType.
596598
*
597599
*/
598-
def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
600+
def instantiate(tp1: NamedType, tp2: Type)(implicit ctx: Context): Type = {
599601
// expose abstract type references to their bounds or tvars according to variance
600-
abstract class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap {
602+
class AbstractTypeMap(maximize: Boolean)(implicit ctx: Context) extends TypeMap {
601603
def expose(tp: TypeRef): Type = {
602604
val lo = this(tp.info.loBound)
603605
val hi = this(tp.info.hiBound)
@@ -613,7 +615,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
613615
exposed
614616
}
615617

616-
override def mapOver(tp: Type): Type = tp match {
618+
def apply(tp: Type): Type = tp match {
617619
case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
618620
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
619621
expose(tp)
@@ -634,28 +636,31 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
634636
exposed
635637

636638
case _ =>
637-
super.mapOver(tp)
638-
}
639-
}
640-
641-
// We are checking the possibility of `tp1 <:< tp2`, thus we should
642-
// minimize `tp1` while maximizing `tp2`. See tests/patmat/3645b.scala
643-
def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) {
644-
def apply(t: Type): Type = t.dealias match {
645-
// map `ThisType` of `tp1` to a type variable
646-
// precondition: `tp1` should have the same shape as `path.Child`, thus `ThisType` is always covariant
647-
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
648-
if (tref.symbol.is(Module)) this(tref)
649-
else newTypeVar(TypeBounds.upper(tp.underlying))
650-
651-
case tp =>
652639
mapOver(tp)
653640
}
654641
}
655642

656-
// replace type parameter references with bounds
657-
def parentTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true) {
658-
def apply(tp: Type): Type = mapOver(tp.dealias)
643+
def minTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false)
644+
def maxTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true)
645+
646+
// Fix subtype checking for child instantiation,
647+
// such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
648+
// See tests/patmat/i3938.scala
649+
def removeThisType(implicit ctx: Context) = new TypeMap {
650+
// is in tvarBounds? Don't create new tvars if true
651+
private var tvarBounds: Boolean = false
652+
def apply(tp: Type): Type = tp match {
653+
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
654+
if (tref.symbol.is(Module))
655+
TermRef(this(tref.prefix), tref.symbol.sourceModule)
656+
else if (tvarBounds)
657+
this(tref)
658+
else {
659+
tvarBounds = true
660+
newTypeVar(TypeBounds.upper(this(tref)))
661+
}
662+
case tp => mapOver(tp)
663+
}
659664
}
660665

661666
// replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
@@ -672,24 +677,24 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
672677
)
673678

674679
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
675-
val protoTp1 = childTypeMap.apply(tp1.appliedTo(tvars))
680+
val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars)
676681

677682
// If parent contains a reference to an abstract type, then we should
678683
// refine subtype checking to eliminate abstract types according to
679684
// variance. As this logic is only needed in exhaustivity check,
680685
// we manually patch subtyping check instead of changing TypeComparer.
681-
// See tests/patmat/3645b.scala
686+
// See tests/patmat/i3645b.scala
682687
def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
683688
implicit val ictx = ctx.fresh.setNewTyperState()
684-
parent.argInfos.nonEmpty && childTypeMap.apply(parent) <:< parentTypeMap.apply(tp2)
689+
parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
685690
}
686691

687692
if (protoTp1 <:< tp2) {
688693
if (isFullyDefined(protoTp1, force)) protoTp1
689694
else instUndetMap.apply(protoTp1)
690695
}
691696
else {
692-
val protoTp2 = parentTypeMap.apply(tp2)
697+
val protoTp2 = maxTypeMap.apply(tp2)
693698
if (protoTp1 <:< protoTp2 || parentQualify) {
694699
if (isFullyDefined(AndType(protoTp1, protoTp2), force)) protoTp1
695700
else instUndetMap.apply(protoTp1)

tests/patmat/i3938.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* subtyping logs
2+
==> isSubType Test.this.foo.Bar(Test.this.foo.bar) <:< Foo.this.Bar?
3+
==> isSubType Test.this.foo.Bar <:< Foo.this.Bar?
4+
==> isSubType Foo(Test.this.foo) <:< Foo(Foo.this)?
5+
==> isSubType Foo <:< Foo(Foo.this)?
6+
<== isSubType Foo <:< Foo(Foo.this) = false
7+
<== isSubType Foo(Test.this.foo) <:< Foo(Foo.this) = false
8+
<== isSubType Test.this.foo.Bar <:< Foo.this.Bar = false
9+
<== isSubType Test.this.foo.Bar(Test.this.foo.bar) <:< Foo.this.Bar = false
10+
*/
11+
12+
13+
class Foo {
14+
val bar = new Bar
15+
class Bar {
16+
sealed abstract class A
17+
case class B() extends A
18+
case class C() extends A
19+
}
20+
}
21+
22+
class Test {
23+
val foo = new Foo
24+
import foo.bar._
25+
26+
def test(a: A) = {
27+
a match {
28+
case B() => 1
29+
case _ => 2 // unreachable code
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)