From 450d0c4f1488d1508697508b6dd1ecdca4555091 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sun, 5 Aug 2018 20:08:28 +0200 Subject: [PATCH 1/3] Fix #4880: always instantiate prefix tvar --- .../tools/dotc/transform/patmat/Space.scala | 23 +++++++++++-------- tests/patmat/i4880.check | 1 + tests/patmat/i4880.scala | 13 +++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 tests/patmat/i4880.check create mode 100644 tests/patmat/i4880.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index a3cfefb12d07..a00dcebdba46 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -656,18 +656,18 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // Fix subtype checking for child instantiation, // such that `Foo(Test.this.foo) <:< Foo(Foo.this)` // See tests/patmat/i3938.scala - def removeThisType(implicit ctx: Context) = new TypeMap { - // is in tvarBounds? Don't create new tvars if true - private var tvarBounds: Boolean = false + class RemoveThisMap extends TypeMap { + var prefixTVar: Type = null def apply(tp: Type): Type = tp match { case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner => if (tref.symbol.is(Module)) TermRef(this(tref.prefix), tref.symbol.sourceModule) - else if (tvarBounds) + else if (prefixTVar != null) this(tref) else { - tvarBounds = true - newTypeVar(TypeBounds.upper(this(tref))) + prefixTVar = WildcardType // prevent recursive call from assigning it + prefixTVar = newTypeVar(TypeBounds.upper(this(tref))) + prefixTVar } case tp => mapOver(tp) } @@ -681,14 +681,17 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } } + val removeThisType = new RemoveThisMap + val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars) + val force = new ForceDegree.Value( - tvar => !(ctx.typerState.constraint.entry(tvar.origin) eq tvar.origin.underlying), + tvar => + !(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) || + (tvar `eq` removeThisType.prefixTVar), minimizeAll = false ) - val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } - val protoTp1 = removeThisType.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 // variance. As this logic is only needed in exhaustivity check, diff --git a/tests/patmat/i4880.check b/tests/patmat/i4880.check new file mode 100644 index 000000000000..da0a6804ac08 --- /dev/null +++ b/tests/patmat/i4880.check @@ -0,0 +1 @@ +10: Pattern Match Exhaustivity: _: ZipArchive, _: VirtualFile, _: PlainFile diff --git a/tests/patmat/i4880.scala b/tests/patmat/i4880.scala new file mode 100644 index 000000000000..c7e92c3b130d --- /dev/null +++ b/tests/patmat/i4880.scala @@ -0,0 +1,13 @@ +sealed abstract class AbstractFile +class PlainFile(path: String) extends AbstractFile +class VirtualFile(name: String) extends AbstractFile +abstract class ZipArchive(path: String) extends AbstractFile { + sealed abstract class Entry(name: String) extends VirtualFile(name) + class DirEntry(path: String) extends Entry(path) +} + +object Test { + def foo(file: AbstractFile) = file match { + case ze: ZipArchive#Entry => + } +} From fdb9e9981e6922adf40cedcd88b7b0b496a7f255 Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Sun, 12 Aug 2018 12:27:42 +0200 Subject: [PATCH 2/3] add further examples --- tests/patmat/i4880.scala | 2 +- tests/patmat/i4880a.scala | 17 +++++++++++++++++ tests/patmat/i4880b.scala | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/patmat/i4880a.scala create mode 100644 tests/patmat/i4880b.scala diff --git a/tests/patmat/i4880.scala b/tests/patmat/i4880.scala index c7e92c3b130d..29c9e77d6c10 100644 --- a/tests/patmat/i4880.scala +++ b/tests/patmat/i4880.scala @@ -7,7 +7,7 @@ abstract class ZipArchive(path: String) extends AbstractFile { } object Test { - def foo(file: AbstractFile) = file match { + def foo(file: AbstractFile) = file match { case ze: ZipArchive#Entry => } } diff --git a/tests/patmat/i4880a.scala b/tests/patmat/i4880a.scala new file mode 100644 index 000000000000..edd160f1a3ce --- /dev/null +++ b/tests/patmat/i4880a.scala @@ -0,0 +1,17 @@ +sealed abstract class AbstractFile +class PlainFile(path: String) extends AbstractFile +class VirtualFile(name: String) extends AbstractFile +abstract class ZipArchive(path: String) extends AbstractFile { + sealed abstract class Entry(name: String) extends VirtualFile(name) + class DirEntry(path: String) extends Entry(path) +} + +object Test { + def foo(file: AbstractFile) = file match { + case a: PlainFile => + case b: ZipArchive => + case c1: ZipArchive#Entry => + case c1: ZipArchive#DirEntry => + case c: VirtualFile => + } +} diff --git a/tests/patmat/i4880b.scala b/tests/patmat/i4880b.scala new file mode 100644 index 000000000000..a31ffb7c46f2 --- /dev/null +++ b/tests/patmat/i4880b.scala @@ -0,0 +1,16 @@ +sealed abstract class AbstractFile +class PlainFile(path: String) extends AbstractFile +class VirtualFile(name: String) extends AbstractFile +abstract class ZipArchive(path: String) extends AbstractFile { + sealed abstract class Entry(name: String) extends VirtualFile(name) + class DirEntry(path: String) extends Entry(path) +} + +object Test { + def foo(file: AbstractFile) = file match { + case a: PlainFile => + case b: ZipArchive => + case c1: ZipArchive#Entry => + case c: VirtualFile => + } +} From 2f0785d9b904a96b594f072004ea83b1e51931b3 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 13 Aug 2018 15:33:28 +0200 Subject: [PATCH 3/3] avoid minimize in prefix inference --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 5 +++-- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index a00dcebdba46..906642b95434 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -292,7 +292,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { private val nullType = ConstantType(Constant(null)) private val nullSpace = Typ(nullType) - override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type) = { + override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = { val and = AndType(tp1, tp2) // Precondition: !(tp1 <:< tp2) && !(tp2 <:< tp1) // Then, no leaf of the and-type tree `and` is a subtype of `and`. @@ -689,7 +689,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { tvar => !(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) || (tvar `eq` removeThisType.prefixTVar), - minimizeAll = false + minimizeAll = false, + allowBottom = false ) // If parent contains a reference to an abstract type, then we should diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 66adb91941b1..07036a81635a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -115,7 +115,7 @@ object Inferencing { val minimize = force.minimizeAll || variance >= 0 && !( - force == ForceDegree.noBottom && + !force.allowBottom && defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))) if (minimize) instantiate(tvar, fromBelow = true) else toMaximize = true @@ -466,9 +466,9 @@ trait Inferencing { this: Typer => /** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ @sharable object ForceDegree { - class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean) + class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean, val allowBottom: Boolean = true) val none = new Value(_ => false, minimizeAll = false) val all = new Value(_ => true, minimizeAll = false) - val noBottom = new Value(_ => true, minimizeAll = false) + val noBottom = new Value(_ => true, minimizeAll = false, allowBottom = false) }