Skip to content

Commit 3cd94b4

Browse files
committed
Avoid inf recursion in provablyDisjointClasses
1 parent f88f92e commit 3cd94b4

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

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

+15-5
Original file line numberDiff line numberDiff line change
@@ -3201,14 +3201,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32013201
result
32023202
end existsCommonBaseTypeWithDisjointArguments
32033203

3204-
provablyDisjointClasses(cls1, cls2)
3204+
provablyDisjointClasses(cls1, cls2, seen = null)
32053205
|| existsCommonBaseTypeWithDisjointArguments
32063206
end match
32073207
}
32083208

3209-
private def provablyDisjointClasses(cls1: Symbol, cls2: Symbol)(using Context): Boolean =
3209+
private def provablyDisjointClasses(cls1: Symbol, cls2: Symbol, seen: util.HashSet[Symbol] | Null)(using Context): Boolean =
32103210
def isDecomposable(cls: Symbol): Boolean =
3211-
cls.is(Sealed) && !cls.hasAnonymousChild
3211+
if seen != null && seen.contains(cls) then false
3212+
else cls.is(Sealed) && !cls.hasAnonymousChild
32123213

32133214
def decompose(cls: Symbol): List[Symbol] =
32143215
cls.children.flatMap { child =>
@@ -3217,6 +3218,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32173218
else child :: Nil
32183219
}.filter(child => child.exists && child != cls)
32193220

3221+
inline def seeing(inline cls: Symbol)(inline thunk: util.HashSet[Symbol] => Boolean) =
3222+
val seen1 = if seen == null then new util.HashSet[Symbol] else seen
3223+
try
3224+
seen1 += cls
3225+
thunk(seen1)
3226+
finally seen1 -= cls
3227+
32203228
def eitherDerivesFromOther(cls1: Symbol, cls2: Symbol): Boolean =
32213229
cls1.derivesFrom(cls2) || cls2.derivesFrom(cls1)
32223230

@@ -3239,9 +3247,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32393247
// instantiations of `cls1` (terms of the form `new cls1`) are not
32403248
// of type `tp2`. Therefore, we can safely decompose `cls1` using
32413249
// `.children`, even if `cls1` is non abstract.
3242-
decompose(cls1).forall(x => provablyDisjointClasses(x, cls2))
3250+
seeing(cls1): seen1 =>
3251+
decompose(cls1).forall(x => provablyDisjointClasses(x, cls2, seen1))
32433252
else if (isDecomposable(cls2))
3244-
decompose(cls2).forall(x => provablyDisjointClasses(cls1, x))
3253+
seeing(cls2): seen1 =>
3254+
decompose(cls2).forall(x => provablyDisjointClasses(cls1, x, seen1))
32453255
else
32463256
false
32473257
end provablyDisjointClasses

tests/pos/i22266.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
sealed trait NonPolygon
2+
sealed trait Polygon
3+
4+
sealed trait SymmetryAspect
5+
sealed trait RotationalSymmetry extends SymmetryAspect
6+
sealed trait MaybeRotationalSymmetry extends SymmetryAspect
7+
8+
enum Shape:
9+
case Circle extends Shape with NonPolygon with RotationalSymmetry
10+
case Triangle extends Shape with Polygon with MaybeRotationalSymmetry
11+
case Square extends Shape with Polygon with RotationalSymmetry
12+
13+
object Shape:
14+
15+
def hasPolygon(
16+
rotationalSyms: Vector[Shape & RotationalSymmetry],
17+
maybeSyms: Vector[Shape & MaybeRotationalSymmetry]
18+
): Boolean =
19+
val all = rotationalSyms.concat(maybeSyms)
20+
all.exists:
21+
case _: Polygon => true
22+
case _ => false

0 commit comments

Comments
 (0)