Skip to content

Commit

Permalink
Replace mergeIfSuper
Browse files Browse the repository at this point in the history
Replace mergeIfSuper by a different algorithm that is more efficient.
We drop or-summands in both arguments of a lub that are subsumed by the other.
This avoids expesnive recusive calls to lub or expensive comparisons
with union types on the right.
  • Loading branch information
odersky committed Mar 27, 2024
1 parent 829adb7 commit ef3ab60
Showing 1 changed file with 44 additions and 18 deletions.
62 changes: 44 additions & 18 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Expand Up @@ -2363,7 +2363,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp1
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp2
else
def mergedLub(tp1: Type, tp2: Type): Type = {
def mergedLub(tp1: Type, tp2: Type): Type =
// First, if tp1 and tp2 are the same singleton type, return one of them.
if tp1.isSingleton && isSubType(tp1, tp2, whenFrozen = !canConstrain) then
return tp2
if tp2.isSingleton && isSubType(tp2, tp1, whenFrozen = !canConstrain) then
return tp1

// Second, handle special cases when tp1 and tp2 are disjunctions of
// singleton types. This saves time otherwise spent in
// costly subtype comparisons performed in dropIfSub below.
tp1.atoms match
case Atoms.Range(lo1, hi1) if !widenInUnions =>
tp2.atoms match
Expand All @@ -2373,18 +2382,22 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
if (hi1 & hi2).isEmpty then return orType(tp1, tp2, isSoft = isSoft)
case none =>
case none =>
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
if (t1.exists) return t1

val t2 = mergeIfSuper(tp2, tp1, canConstrain)
if (t2.exists) return t2

def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
// Third, try to simplify after widening as follows:
// 1. Drop all or-factors in tp2 that are subtypes of an or-factor
// in tp1, yielding tp2Final.
// 2. Drop all or-factors in tp1 that are subtypes of an or-factor
// in tp2Final, yielding tp1Final.
// 3. Combine the two final types in an OrType
def widen(tp: Type) =
if widenInUnions then tp.widen else tp.widenIfUnstable
val tp1w = widen(tp1)
val tp2w = widen(tp2)
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w, canConstrain = canConstrain, isSoft = isSoft)
else orType(tp1w, tp2w, isSoft = isSoft) // no need to check subtypes again
}
val tp2Final = dropIfSub(tp2w, tp1w, canConstrain)
val tp1Final = dropIfSub(tp1w, tp2Final, canConstrain)
recombine(tp1Final, tp2Final, orType(_, _, isSoft = isSoft))
end mergedLub

mergedLub(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
}

Expand Down Expand Up @@ -2448,21 +2461,34 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
Nil
}

private def recombineAnd(tp: AndType, tp1: Type, tp2: Type) =
if (!tp1.exists) tp2
else if (!tp2.exists) tp1
else tp.derivedAndType(tp1, tp2)
private def recombine(tp1: Type, tp2: Type, rebuild: (Type, Type) => Type): Type =
if !tp1.exists then tp2
else if !tp2.exists then tp1
else rebuild(tp1, tp2)


private def recombine(tp: AndOrType, tp1: Type, tp2: Type): Type =
recombine(tp1, tp2, tp.derivedAndOrType)

/** If some (&-operand of) `tp` is a supertype of `sub` replace it with `NoType`.
*/
private def dropIfSuper(tp: Type, sub: Type): Type =
if (isSubTypeWhenFrozen(sub, tp)) NoType
else tp match {
if isSubTypeWhenFrozen(sub, tp) then NoType
else tp match
case tp @ AndType(tp1, tp2) =>
recombineAnd(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
recombine(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
case _ =>
tp
}

private def dropIfSub(tp: Type, sup: Type, canConstrain: Boolean): Type =
def isSub(sup: Type): Boolean = sup.stripTypeVar match
case OrType(sup1, sup2) => isSub(sup1) || isSub(sup2)
case _ => isSubType(tp, sup, whenFrozen = !canConstrain)
tp.stripTypeVar match
case tp @ OrType(tp1, tp2) =>
recombine(tp, dropIfSub(tp1, sup, canConstrain), dropIfSub(tp2, sup, canConstrain))
case _ =>
if isSub(sup) then NoType else tp

/** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2.
*/
Expand Down

0 comments on commit ef3ab60

Please sign in to comment.