From 6fa9a4cd847f479ac3f0094191d1b3e60ded0d7a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 22 Jan 2021 16:20:34 +0100 Subject: [PATCH 1/3] Fix #11078: avoid widening F-bounds in type avoidance --- .../src/dotty/tools/dotc/core/TypeOps.scala | 30 ++++++++++++++++++- tests/pos/i11078.scala | 29 ++++++++++++++++++ tests/pos/i11078b.scala | 11 +++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i11078.scala create mode 100644 tests/pos/i11078b.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index cfe3b80a2422..22cea7c716d5 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -3,7 +3,7 @@ package dotc package core import Contexts._, Types._, Symbols._, Names._, Flags._ -import SymDenotations._ +import Denotations._, SymDenotations._ import util.Spans._ import util.Stats import NameKinds.DepParamName @@ -462,6 +462,34 @@ object TypeOps: else range(defn.NothingType, defn.AnyType) } + + /** Deviation from standard tryWiden: + * - Don't widen F-bounds + */ + override def tryWiden(tp: NamedType, pre: Type): Type = pre.member(tp.name) match { + case d: SingleDenotation => + val tp1 = d.info.dealiasKeepAnnots + tp1.stripAnnots match { + case TypeAlias(alias) => + // if H#T = U, then for any x in L..H, x.T =:= U, + // hence we can replace with U under all variances + reapply(alias.rewrapAnnots(tp1)) + + case tb: TypeBounds => + // Don't widen F-bounds + val isFBounds = tb.existsPart(p => p.isInstanceOf[LazyRef], forceLazy = false) + if isFBounds then NoType + else expandBounds(tb) + case info: SingletonType => + // if H#x: y.type, then for any x in L..H, x.type =:= y.type, + // hence we can replace with y.type under all variances + reapply(info) + case _ => + NoType + } + case _ => NoType + } + } widenMap(tp) diff --git a/tests/pos/i11078.scala b/tests/pos/i11078.scala new file mode 100644 index 000000000000..f4b6669d30f4 --- /dev/null +++ b/tests/pos/i11078.scala @@ -0,0 +1,29 @@ +trait Foo[A <: Foo[A]] +trait FooCreator[A <: Foo[A]] { + def createFoo(): A +} + +trait FooWrapper { + type A <: Foo[A] + def foo: A +} +object FooWrapper { + def apply[A0 <: Foo[A0]](toWrap: A0): FooWrapper { type A = A0 } = new FooWrapper { + type A = A0 + def foo: A0 = toWrap + } +} + +trait FooCreatorWrapper { + type A <: Foo[A] + def fooCreator: FooCreator[A] +} + +sealed trait Bar +object Bar { + case class Baz(wrapper: FooCreatorWrapper) extends Bar +} + +def process(bar: Bar): FooWrapper = bar match { + case Bar.Baz(wrapper) => FooWrapper(wrapper.fooCreator.createFoo()) +} \ No newline at end of file diff --git a/tests/pos/i11078b.scala b/tests/pos/i11078b.scala new file mode 100644 index 000000000000..e14453dc5a7c --- /dev/null +++ b/tests/pos/i11078b.scala @@ -0,0 +1,11 @@ +class Test { + trait Foo[A <: Foo[A]] + + trait FooWrapper with self => + type A <: Foo[A] + def doThing(foo: FooWrapper): FooWrapper { type A = self.A } = ??? + end FooWrapper + + val foos: scala.Seq[FooWrapper] = ??? + val newFoo = foos.foldLeft(??? : FooWrapper)((topFoo, foo) => topFoo.doThing(foo)) +} From 9d1257e72d0a115001698cb258aa26406d0e0371 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 22 Jan 2021 22:09:44 +0100 Subject: [PATCH 2/3] Trial 2: use WildcardType for LazyRef in type avoidance --- .../src/dotty/tools/dotc/core/TypeOps.scala | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 22cea7c716d5..6c6443798c10 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -438,6 +438,8 @@ object TypeOps: tp.origin, fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound)(using mapCtx) val lo1 = apply(lo) if (lo1 ne lo) lo1 else tp + case tp: LazyRef => + TypeBounds.empty case _ => mapOver(tp) } @@ -462,34 +464,6 @@ object TypeOps: else range(defn.NothingType, defn.AnyType) } - - /** Deviation from standard tryWiden: - * - Don't widen F-bounds - */ - override def tryWiden(tp: NamedType, pre: Type): Type = pre.member(tp.name) match { - case d: SingleDenotation => - val tp1 = d.info.dealiasKeepAnnots - tp1.stripAnnots match { - case TypeAlias(alias) => - // if H#T = U, then for any x in L..H, x.T =:= U, - // hence we can replace with U under all variances - reapply(alias.rewrapAnnots(tp1)) - - case tb: TypeBounds => - // Don't widen F-bounds - val isFBounds = tb.existsPart(p => p.isInstanceOf[LazyRef], forceLazy = false) - if isFBounds then NoType - else expandBounds(tb) - case info: SingletonType => - // if H#x: y.type, then for any x in L..H, x.type =:= y.type, - // hence we can replace with y.type under all variances - reapply(info) - case _ => - NoType - } - case _ => NoType - } - } widenMap(tp) From f661681ac6035d1207c85ee3db9b9ad161585ffb Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 25 Jan 2021 11:22:41 +0100 Subject: [PATCH 3/3] Trial 3: don't follow LazyRef in expanding bounds --- .../src/dotty/tools/dotc/core/TypeOps.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 6c6443798c10..9568698893b7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -438,8 +438,8 @@ object TypeOps: tp.origin, fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound)(using mapCtx) val lo1 = apply(lo) if (lo1 ne lo) lo1 else tp - case tp: LazyRef => - TypeBounds.empty + case tp: LazyRef if isExpandingBounds => + emptyRange case _ => mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 782a92624fe5..59f8c37cbf55 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5328,8 +5328,23 @@ object Types { case _ => tp } + private var expandingBounds: Boolean = false + + /** Whether it is currently expanding bounds + * + * It is used to avoid following LazyRef in F-Bounds + */ + def isExpandingBounds: Boolean = expandingBounds + protected def expandBounds(tp: TypeBounds): Type = - range(atVariance(-variance)(reapply(tp.lo)), reapply(tp.hi)) + if expandingBounds then tp + else { + val saved = expandingBounds + expandingBounds = true + val res = range(atVariance(-variance)(reapply(tp.lo)), reapply(tp.hi)) + expandingBounds = saved + res + } /** Try to widen a named type to its info relative to given prefix `pre`, where possible. * The possible cases are listed inline in the code.