Skip to content

Commit ef3ab60

Browse files
committed
Replace mergeIfSuper
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.
1 parent 829adb7 commit ef3ab60

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

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

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2363,7 +2363,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23632363
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp1
23642364
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp2
23652365
else
2366-
def mergedLub(tp1: Type, tp2: Type): Type = {
2366+
def mergedLub(tp1: Type, tp2: Type): Type =
2367+
// First, if tp1 and tp2 are the same singleton type, return one of them.
2368+
if tp1.isSingleton && isSubType(tp1, tp2, whenFrozen = !canConstrain) then
2369+
return tp2
2370+
if tp2.isSingleton && isSubType(tp2, tp1, whenFrozen = !canConstrain) then
2371+
return tp1
2372+
2373+
// Second, handle special cases when tp1 and tp2 are disjunctions of
2374+
// singleton types. This saves time otherwise spent in
2375+
// costly subtype comparisons performed in dropIfSub below.
23672376
tp1.atoms match
23682377
case Atoms.Range(lo1, hi1) if !widenInUnions =>
23692378
tp2.atoms match
@@ -2373,18 +2382,22 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23732382
if (hi1 & hi2).isEmpty then return orType(tp1, tp2, isSoft = isSoft)
23742383
case none =>
23752384
case none =>
2376-
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
2377-
if (t1.exists) return t1
23782385

2379-
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
2380-
if (t2.exists) return t2
2381-
2382-
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
2386+
// Third, try to simplify after widening as follows:
2387+
// 1. Drop all or-factors in tp2 that are subtypes of an or-factor
2388+
// in tp1, yielding tp2Final.
2389+
// 2. Drop all or-factors in tp1 that are subtypes of an or-factor
2390+
// in tp2Final, yielding tp1Final.
2391+
// 3. Combine the two final types in an OrType
2392+
def widen(tp: Type) =
2393+
if widenInUnions then tp.widen else tp.widenIfUnstable
23832394
val tp1w = widen(tp1)
23842395
val tp2w = widen(tp2)
2385-
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w, canConstrain = canConstrain, isSoft = isSoft)
2386-
else orType(tp1w, tp2w, isSoft = isSoft) // no need to check subtypes again
2387-
}
2396+
val tp2Final = dropIfSub(tp2w, tp1w, canConstrain)
2397+
val tp1Final = dropIfSub(tp1w, tp2Final, canConstrain)
2398+
recombine(tp1Final, tp2Final, orType(_, _, isSoft = isSoft))
2399+
end mergedLub
2400+
23882401
mergedLub(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
23892402
}
23902403

@@ -2448,21 +2461,34 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
24482461
Nil
24492462
}
24502463

2451-
private def recombineAnd(tp: AndType, tp1: Type, tp2: Type) =
2452-
if (!tp1.exists) tp2
2453-
else if (!tp2.exists) tp1
2454-
else tp.derivedAndType(tp1, tp2)
2464+
private def recombine(tp1: Type, tp2: Type, rebuild: (Type, Type) => Type): Type =
2465+
if !tp1.exists then tp2
2466+
else if !tp2.exists then tp1
2467+
else rebuild(tp1, tp2)
2468+
2469+
2470+
private def recombine(tp: AndOrType, tp1: Type, tp2: Type): Type =
2471+
recombine(tp1, tp2, tp.derivedAndOrType)
24552472

24562473
/** If some (&-operand of) `tp` is a supertype of `sub` replace it with `NoType`.
24572474
*/
24582475
private def dropIfSuper(tp: Type, sub: Type): Type =
2459-
if (isSubTypeWhenFrozen(sub, tp)) NoType
2460-
else tp match {
2476+
if isSubTypeWhenFrozen(sub, tp) then NoType
2477+
else tp match
24612478
case tp @ AndType(tp1, tp2) =>
2462-
recombineAnd(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
2479+
recombine(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
24632480
case _ =>
24642481
tp
2465-
}
2482+
2483+
private def dropIfSub(tp: Type, sup: Type, canConstrain: Boolean): Type =
2484+
def isSub(sup: Type): Boolean = sup.stripTypeVar match
2485+
case OrType(sup1, sup2) => isSub(sup1) || isSub(sup2)
2486+
case _ => isSubType(tp, sup, whenFrozen = !canConstrain)
2487+
tp.stripTypeVar match
2488+
case tp @ OrType(tp1, tp2) =>
2489+
recombine(tp, dropIfSub(tp1, sup, canConstrain), dropIfSub(tp2, sup, canConstrain))
2490+
case _ =>
2491+
if isSub(sup) then NoType else tp
24662492

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

0 commit comments

Comments
 (0)