Skip to content

Commit 380bd1b

Browse files
committed
Speedup isCovered
- Revert tailrec rewrite to reduce allocation - Use a integer-based representation for the result of `isCovered` The results are now aggregated and inspected with bitwise operations.
1 parent 455aa50 commit 380bd1b

File tree

1 file changed

+37
-24
lines changed

1 file changed

+37
-24
lines changed

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

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,30 +1488,18 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
14881488
* See #17465.
14891489
*/
14901490
def isNewSubType(tp1: Type): Boolean =
1491-
1492-
def isCovered(tp: Type): (Boolean, Boolean) =
1493-
var containsOr: Boolean = false
1494-
@annotation.tailrec def recur(todos: List[Type]): Boolean = todos match
1495-
case tp :: todos =>
1496-
tp.dealiasKeepRefiningAnnots.stripTypeVar match
1497-
case tp: TypeRef =>
1498-
if tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass then recur(todos)
1499-
else false
1500-
case tp: AppliedType => recur(tp.tycon :: todos)
1501-
case tp: RefinedOrRecType => recur(tp.parent :: todos)
1502-
case tp: AndType => recur(tp.tp1 :: tp.tp2 :: todos)
1503-
case tp: OrType =>
1504-
containsOr = true
1505-
recur(tp.tp1 :: tp.tp2 :: todos)
1506-
case _ => false
1507-
case Nil => true
1508-
val result = recur(tp :: Nil)
1509-
(result, containsOr)
1510-
1511-
val (covered1, hasOr1) = isCovered(tp1)
1512-
val (covered2, hasOr2) = isCovered(tp2)
1513-
1514-
if covered1 && covered2 && !(hasOr1 && hasOr2) then
1491+
def isCovered(tp: Type): CoveredStatus =
1492+
tp.dealiasKeepRefiningAnnots.stripTypeVar match
1493+
case tp: TypeRef if tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass => CoveredStatus.Covered
1494+
case tp: AppliedType => isCovered(tp.tycon)
1495+
case tp: RefinedOrRecType => isCovered(tp.parent)
1496+
case tp: AndType => isCovered(tp.tp1) combinedWith isCovered(tp.tp2)
1497+
case tp: OrType => isCovered(tp.tp1) combinedWith isCovered(tp.tp2)
1498+
case _ => CoveredStatus.Uncovered
1499+
1500+
val covered1 = isCovered(tp1)
1501+
val covered2 = isCovered(tp2)
1502+
if CoveredStatus.bothCovered(covered1, covered2) && !CoveredStatus.bothHaveOr(covered1, covered2) then
15151503
//println(s"useless subtype: $tp1 <:< $tp2")
15161504
false
15171505
else isSubType(tp1, tp2, approx.addLow)
@@ -3008,6 +2996,31 @@ object TypeComparer {
30082996
end ApproxState
30092997
type ApproxState = ApproxState.Repr
30102998

2999+
/** Result of `isCovered` check. */
3000+
object CoveredStatus:
3001+
opaque type Repr = Int
3002+
3003+
private inline val IsCovered = 2
3004+
private inline val NotHasOr = 1
3005+
3006+
/** The type is not covered. */
3007+
val Uncovered: Repr = 1
3008+
3009+
/** The type is covered and contains OrTypes. */
3010+
val CoveredWithOr: Repr = 2
3011+
3012+
/** The type is covered and free from OrTypes. */
3013+
val Covered: Repr = 3
3014+
3015+
object Repr:
3016+
extension (s: Repr)
3017+
inline def combinedWith(that: Repr): Repr = s min that
3018+
3019+
inline def bothHaveOr(s1: Repr, s2: Repr): Boolean = ~(s1 | s2 & NotHasOr) != 0
3020+
inline def bothCovered(s1: Repr, s2: Repr): Boolean = (s1 & s2 & IsCovered) != 0
3021+
end CoveredStatus
3022+
type CoveredStatus = CoveredStatus.Repr
3023+
30113024
def topLevelSubType(tp1: Type, tp2: Type)(using Context): Boolean =
30123025
comparing(_.topLevelSubType(tp1, tp2))
30133026

0 commit comments

Comments
 (0)