Skip to content

Commit b4670b7

Browse files
authored
Merge pull request #4897 from dotty-staging/fix-4880
Fix #4880: always instantiate prefix tvar
2 parents a91d82a + 2f0785d commit b4670b7

File tree

6 files changed

+66
-15
lines changed

6 files changed

+66
-15
lines changed

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
292292
private val nullType = ConstantType(Constant(null))
293293
private val nullSpace = Typ(nullType)
294294

295-
override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type) = {
295+
override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = {
296296
val and = AndType(tp1, tp2)
297297
// Precondition: !(tp1 <:< tp2) && !(tp2 <:< tp1)
298298
// Then, no leaf of the and-type tree `and` is a subtype of `and`.
@@ -656,18 +656,18 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
656656
// Fix subtype checking for child instantiation,
657657
// such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
658658
// See tests/patmat/i3938.scala
659-
def removeThisType(implicit ctx: Context) = new TypeMap {
660-
// is in tvarBounds? Don't create new tvars if true
661-
private var tvarBounds: Boolean = false
659+
class RemoveThisMap extends TypeMap {
660+
var prefixTVar: Type = null
662661
def apply(tp: Type): Type = tp match {
663662
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
664663
if (tref.symbol.is(Module))
665664
TermRef(this(tref.prefix), tref.symbol.sourceModule)
666-
else if (tvarBounds)
665+
else if (prefixTVar != null)
667666
this(tref)
668667
else {
669-
tvarBounds = true
670-
newTypeVar(TypeBounds.upper(this(tref)))
668+
prefixTVar = WildcardType // prevent recursive call from assigning it
669+
prefixTVar = newTypeVar(TypeBounds.upper(this(tref)))
670+
prefixTVar
671671
}
672672
case tp => mapOver(tp)
673673
}
@@ -681,14 +681,18 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
681681
}
682682
}
683683

684-
val force = new ForceDegree.Value(
685-
tvar => !(ctx.typerState.constraint.entry(tvar.origin) eq tvar.origin.underlying),
686-
minimizeAll = false
687-
)
688-
684+
val removeThisType = new RemoveThisMap
689685
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
690686
val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars)
691687

688+
val force = new ForceDegree.Value(
689+
tvar =>
690+
!(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
691+
(tvar `eq` removeThisType.prefixTVar),
692+
minimizeAll = false,
693+
allowBottom = false
694+
)
695+
692696
// If parent contains a reference to an abstract type, then we should
693697
// refine subtype checking to eliminate abstract types according to
694698
// variance. As this logic is only needed in exhaustivity check,

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ object Inferencing {
115115
val minimize =
116116
force.minimizeAll ||
117117
variance >= 0 && !(
118-
force == ForceDegree.noBottom &&
118+
!force.allowBottom &&
119119
defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true)))
120120
if (minimize) instantiate(tvar, fromBelow = true)
121121
else toMaximize = true
@@ -466,9 +466,9 @@ trait Inferencing { this: Typer =>
466466

467467
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
468468
@sharable object ForceDegree {
469-
class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean)
469+
class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean, val allowBottom: Boolean = true)
470470
val none = new Value(_ => false, minimizeAll = false)
471471
val all = new Value(_ => true, minimizeAll = false)
472-
val noBottom = new Value(_ => true, minimizeAll = false)
472+
val noBottom = new Value(_ => true, minimizeAll = false, allowBottom = false)
473473
}
474474

tests/patmat/i4880.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10: Pattern Match Exhaustivity: _: ZipArchive, _: VirtualFile, _: PlainFile

tests/patmat/i4880.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
sealed abstract class AbstractFile
2+
class PlainFile(path: String) extends AbstractFile
3+
class VirtualFile(name: String) extends AbstractFile
4+
abstract class ZipArchive(path: String) extends AbstractFile {
5+
sealed abstract class Entry(name: String) extends VirtualFile(name)
6+
class DirEntry(path: String) extends Entry(path)
7+
}
8+
9+
object Test {
10+
def foo(file: AbstractFile) = file match {
11+
case ze: ZipArchive#Entry =>
12+
}
13+
}

tests/patmat/i4880a.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
sealed abstract class AbstractFile
2+
class PlainFile(path: String) extends AbstractFile
3+
class VirtualFile(name: String) extends AbstractFile
4+
abstract class ZipArchive(path: String) extends AbstractFile {
5+
sealed abstract class Entry(name: String) extends VirtualFile(name)
6+
class DirEntry(path: String) extends Entry(path)
7+
}
8+
9+
object Test {
10+
def foo(file: AbstractFile) = file match {
11+
case a: PlainFile =>
12+
case b: ZipArchive =>
13+
case c1: ZipArchive#Entry =>
14+
case c1: ZipArchive#DirEntry =>
15+
case c: VirtualFile =>
16+
}
17+
}

tests/patmat/i4880b.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
sealed abstract class AbstractFile
2+
class PlainFile(path: String) extends AbstractFile
3+
class VirtualFile(name: String) extends AbstractFile
4+
abstract class ZipArchive(path: String) extends AbstractFile {
5+
sealed abstract class Entry(name: String) extends VirtualFile(name)
6+
class DirEntry(path: String) extends Entry(path)
7+
}
8+
9+
object Test {
10+
def foo(file: AbstractFile) = file match {
11+
case a: PlainFile =>
12+
case b: ZipArchive =>
13+
case c1: ZipArchive#Entry =>
14+
case c: VirtualFile =>
15+
}
16+
}

0 commit comments

Comments
 (0)