Skip to content

Commit a6eac21

Browse files
committed
Recursion brake for joins in TypeComparer
Don't recursively apply joins on the left hand side if it contains an AndType, in order to prevent possible exponential explosions. Fixes #14870
1 parent 8d3083b commit a6eac21

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
158158
private inline def inFrozenGadt[T](inline op: T): T =
159159
inFrozenGadtIf(true)(op)
160160

161+
/** A flag to prevent recursive joins when comparing AndTypes on the left */
162+
private var joined = false
163+
161164
private inline def inFrozenGadtIf[T](cond: Boolean)(inline op: T): T = {
162165
val savedFrozenGadt = frozenGadt
163166
frozenGadt ||= cond
@@ -477,11 +480,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
477480
widenOK
478481
|| joinOK
479482
|| (tp1.isSoft || constrainRHSVars(tp2)) && recur(tp11, tp2) && recur(tp12, tp2)
480-
|| containsAnd(tp1) && inFrozenGadt(recur(tp1.join, tp2))
483+
|| containsAnd(tp1)
484+
&& !joined
485+
&& {
486+
joined = true
487+
try inFrozenGadt(recur(tp1.join, tp2))
488+
finally joined = false
489+
}
481490
// An & on the left side loses information. We compensate by also trying the join.
482491
// This is less ad-hoc than it looks since we produce joins in type inference,
483492
// and then need to check that they are indeed supertypes of the original types
484493
// under -Ycheck. Test case is i7965.scala.
494+
// On the other hand, we could get a combinatorial explosion by applying such joins
495+
// recursively, so we do it only once. See i14870.scala as a test case, which would
496+
// loop for a very long time without the recursion brake.
485497

486498
case tp1: MatchType =>
487499
val reduced = tp1.reduced

tests/pos-deep-subtype/i14870.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
abstract class Quantity[A <: Quantity[A]]
2+
class Energy extends Quantity[Energy]
3+
class Time extends Quantity[Time]
4+
class Dimensionless extends Quantity[Dimensionless]
5+
6+
class Price[Q <: Quantity[Q] & (Energy | Time | Dimensionless)]

0 commit comments

Comments
 (0)