Skip to content

Commit

Permalink
Break circularity in TypeBounds with LazyRef wraps
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jan 27, 2022
1 parent 7939ddb commit 229f065
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,20 @@ trait ConstraintHandling {
* of some param when comparing types might lead to infinite recursion. Consider `bounds` instead.
*/
def fullBounds(param: TypeParamRef)(using Context): TypeBounds =
nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
val lo = wrapRecursiveInLazy(fullLowerBound(param))
val hi = wrapRecursiveInLazy(fullUpperBound(param))
nonParamBounds(param).derivedTypeBounds(lo, hi)

def wrapRecursiveInLazy(tp: Type)(using Context) =
if new IsRecursiveAccumulator(tp, StopAt.None).run(tp) then LazyRef.of(tp)
else tp

class IsRecursiveAccumulator(targetTp: Type, override val stopAt: StopAt)(using Context) extends TypeAccumulator[Boolean]:
def apply(x: Boolean, tp: Type): Boolean = x || (tp eq targetTp) || run(tp)
def run(tp: Type): Boolean = tp match
case tp: LazyRef => false
case tp @ TypeRef(NoPrefix, _) => apply(false, tp.info)
case _ => foldOver(false, tp)

/** An approximating map that prevents types nested deeper than maxLevel as
* well as WildcardTypes from leaking into the constraint.
Expand Down
6 changes: 6 additions & 0 deletions tests/pos/i14287.min.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum Foo[+H[_]]:
case Bar[F[_]](f: Foo[F]) extends Foo[F]

def test: Foo[H] = this match
case Bar(Bar(f)) => Bar(f)
case _ => this
15 changes: 15 additions & 0 deletions tests/pos/i14287.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum Free[+F[_], A]:
case Return(a: A)
case Suspend(s: F[A])
case FlatMap[F[_], A, B](
s: Free[F, A],
f: A => Free[F, B]) extends Free[F, B]

def flatMap[F2[x] >: F[x], B](f: A => Free[F2,B]): Free[F2,B] =
FlatMap(this, f)

@annotation.tailrec
final def step: Free[F, A] = this match
case FlatMap(FlatMap(fx, f), g) => fx.flatMap(x => f(x).flatMap(y => g(y))).step
case FlatMap(Return(x), f) => f(x).step
case _ => this

0 comments on commit 229f065

Please sign in to comment.