Skip to content

Commit b19ea47

Browse files
oderskyallanrenucci
authored andcommitted
Optimize occursIn
Analyzing tuples2 has shown that there's a quadratic blowup due to the occursIn check in ConstraintHandling#approximate. This change avoids traversing types for occursIn as long as they are ground.
1 parent 0cdb63d commit b19ea47

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,11 @@ object Types {
301301
}
302302

303303
/** Does this type occur as a part of type `that`? */
304-
final def occursIn(that: Type)(implicit ctx: Context): Boolean =
305-
that existsPart (this == _)
304+
def occursIn(that: Type)(implicit ctx: Context): Boolean =
305+
that.existsPart(this == _)
306+
307+
/** Does this type not refer to TypeParamRefs or uninstantiated TypeVars? */
308+
final def isGround(implicit ctx: Context): Boolean = true
306309

307310
/** Is this a type of a repeated parameter? */
308311
def isRepeatedParam(implicit ctx: Context): Boolean =
@@ -3271,6 +3274,17 @@ object Types {
32713274
private[this] var cachedSuper: Type = _
32723275
private[this] var myStableHash: Byte = 0
32733276

3277+
private[this] var isGroundKnown: Boolean = false
3278+
private[this] var isGroundCache: Boolean = _
3279+
3280+
def isGround(acc: TypeAccumulator[Boolean])(implicit ctx: Context): Boolean = {
3281+
if (!isGroundKnown) {
3282+
isGroundCache = acc.foldOver(false, this)
3283+
isGroundKnown = true
3284+
}
3285+
isGroundCache
3286+
}
3287+
32743288
override def underlying(implicit ctx: Context): Type = tycon
32753289

32763290
override def superType(implicit ctx: Context): Type = {
@@ -3414,6 +3428,11 @@ object Types {
34143428
def kindString = "Type"
34153429
def copyBoundType(bt: BT) = bt.paramRefs(paramNum)
34163430

3431+
/** Optimized version of occursIn, avoid quadratic blowup when solving
3432+
* constraints over large ground types.
3433+
*/
3434+
override def occursIn(that: Type)(implicit ctx: Context) = !that.isGround && super.occursIn(that)
3435+
34173436
/** Looking only at the structure of `bound`, is one of the following true?
34183437
* - fromBelow and param <:< bound
34193438
* - !fromBelow and param >:> bound
@@ -4777,6 +4796,17 @@ object Types {
47774796
}
47784797
}
47794798

4799+
class isGroundAccumulator(implicit ctx: Context) extends TypeAccumulator[Boolean] {
4800+
def apply(x: Boolean, tp: Type) = x || {
4801+
tp match {
4802+
case _: TypeParamRef => false
4803+
case tp: TypeVar => apply(x, tp.underlying)
4804+
case tp: AppliedType => tp.isGround(this)
4805+
case _ => foldOver(x, tp)
4806+
}
4807+
}
4808+
}
4809+
47804810
// ----- Name Filters --------------------------------------------------
47814811

47824812
/** A name filter selects or discards a member name of a type `pre`.

0 commit comments

Comments
 (0)