Skip to content

Commit 229f065

Browse files
committed
Break circularity in TypeBounds with LazyRef wraps
1 parent 7939ddb commit 229f065

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,20 @@ trait ConstraintHandling {
127127
* of some param when comparing types might lead to infinite recursion. Consider `bounds` instead.
128128
*/
129129
def fullBounds(param: TypeParamRef)(using Context): TypeBounds =
130-
nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
130+
val lo = wrapRecursiveInLazy(fullLowerBound(param))
131+
val hi = wrapRecursiveInLazy(fullUpperBound(param))
132+
nonParamBounds(param).derivedTypeBounds(lo, hi)
133+
134+
def wrapRecursiveInLazy(tp: Type)(using Context) =
135+
if new IsRecursiveAccumulator(tp, StopAt.None).run(tp) then LazyRef.of(tp)
136+
else tp
137+
138+
class IsRecursiveAccumulator(targetTp: Type, override val stopAt: StopAt)(using Context) extends TypeAccumulator[Boolean]:
139+
def apply(x: Boolean, tp: Type): Boolean = x || (tp eq targetTp) || run(tp)
140+
def run(tp: Type): Boolean = tp match
141+
case tp: LazyRef => false
142+
case tp @ TypeRef(NoPrefix, _) => apply(false, tp.info)
143+
case _ => foldOver(false, tp)
131144

132145
/** An approximating map that prevents types nested deeper than maxLevel as
133146
* well as WildcardTypes from leaking into the constraint.

tests/pos/i14287.min.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
enum Foo[+H[_]]:
2+
case Bar[F[_]](f: Foo[F]) extends Foo[F]
3+
4+
def test: Foo[H] = this match
5+
case Bar(Bar(f)) => Bar(f)
6+
case _ => this

tests/pos/i14287.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
enum Free[+F[_], A]:
2+
case Return(a: A)
3+
case Suspend(s: F[A])
4+
case FlatMap[F[_], A, B](
5+
s: Free[F, A],
6+
f: A => Free[F, B]) extends Free[F, B]
7+
8+
def flatMap[F2[x] >: F[x], B](f: A => Free[F2,B]): Free[F2,B] =
9+
FlatMap(this, f)
10+
11+
@annotation.tailrec
12+
final def step: Free[F, A] = this match
13+
case FlatMap(FlatMap(fx, f), g) => fx.flatMap(x => f(x).flatMap(y => g(y))).step
14+
case FlatMap(Return(x), f) => f(x).step
15+
case _ => this

0 commit comments

Comments
 (0)