Skip to content

Commit dab5e51

Browse files
committed
Compute fewer hashes
Before hashing with a non-null binders list, check whether the type has a stable hash. Hash-stability results are cached in NamedTypes and AppliedTypes.
1 parent dc7b25c commit dab5e51

File tree

3 files changed

+54
-32
lines changed

3 files changed

+54
-32
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ trait Hashable {
4343
avoidSpecialHashes(hashing.finalizeHash(hashCode, arity))
4444

4545
final def typeHash(bs: Binders, tp: Type) =
46-
if (bs == null) tp.hash else tp.computeHash(bs)
46+
if (bs == null || tp.stableHash) tp.hash else tp.computeHash(bs)
4747

4848
def identityHash(bs: Binders) = avoidSpecialHashes(System.identityHashCode(this))
4949

@@ -100,7 +100,6 @@ trait Hashable {
100100
protected final def doHash(bs: Binders, x1: Any, tp2: Type, tps3: List[Type]): Int =
101101
finishHash(bs, hashing.mix(hashSeed, x1.hashCode), 1, tp2, tps3)
102102

103-
104103
protected final def doHash(x1: Int, x2: Int): Int =
105104
finishHash(hashing.mix(hashing.mix(hashSeed, x1), x2), 1)
106105

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

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,9 @@ object Types {
14171417
/** Compute hashcode relative to enclosing binders `bs` */
14181418
def computeHash(bs: Binders): Int
14191419

1420+
/** Is the `hash` of this type the same for all possible sequences of enclosing binders? */
1421+
def stableHash: Boolean = true
1422+
14201423
} // end Type
14211424

14221425
// ----- Type categories ----------------------------------------------
@@ -1566,6 +1569,7 @@ object Types {
15661569
private[this] var lastDenotation: Denotation = null
15671570
private[this] var lastSymbol: Symbol = null
15681571
private[this] var checkedPeriod: Period = Nowhere
1572+
private[this] var myStableHash: Byte = 0
15691573

15701574
// Invariants:
15711575
// (1) checkedPeriod != Nowhere => lastDenotation != null
@@ -2038,6 +2042,11 @@ object Types {
20382042

20392043
override def computeHash(bs: Binders) = doHash(bs, designator, prefix)
20402044

2045+
override def stableHash = {
2046+
if (myStableHash == 0) myStableHash = if (prefix.stableHash) 1 else -1
2047+
myStableHash > 0
2048+
}
2049+
20412050
override def eql(that: Type) = this eq that // safe because named types are hash-consed separately
20422051
}
20432052

@@ -2289,6 +2298,7 @@ object Types {
22892298
else parent
22902299

22912300
override def computeHash(bs: Binders) = doHash(bs, refinedName, refinedInfo, parent)
2301+
override def stableHash = refinedInfo.stableHash && parent.stableHash
22922302

22932303
override def eql(that: Type) = that match {
22942304
case that: RefinedType =>
@@ -2362,6 +2372,11 @@ object Types {
23622372

23632373
override def computeHash(bs: Binders) = doHash(new Binders(this, bs), parent)
23642374

2375+
override def stableHash = false
2376+
// this is a conservative observation. By construction RecTypes contain at least
2377+
// one RecThis occurrence. Since `stableHash` does not keep track of enclosing
2378+
// bound types, it will return "unstable" for this occurrence and this would propagate.
2379+
23652380
override def eql(that: Type) = that match {
23662381
case that: RecType => parent.eq(that.parent)
23672382
case _ => false
@@ -2414,7 +2429,7 @@ object Types {
24142429

24152430
// --- AndType/OrType ---------------------------------------------------------------
24162431

2417-
trait AndOrType extends ValueType { // todo: check where we can simplify using AndOrType
2432+
abstract class AndOrType extends CachedGroundType with ValueType {
24182433
def tp1: Type
24192434
def tp2: Type
24202435
def isAnd: Boolean
@@ -2450,9 +2465,22 @@ object Types {
24502465
}
24512466
myBaseClasses
24522467
}
2468+
2469+
override def computeHash(bs: Binders) = doHash(bs, tp1, tp2)
2470+
override def stableHash = tp1.stableHash && tp2.stableHash
2471+
2472+
override def eql(that: Type) = that match {
2473+
case that: AndOrType => isAnd == that.isAnd && tp1.eq(that.tp1) && tp2.eq(that.tp2)
2474+
case _ => false
2475+
}
2476+
2477+
override def iso(that: Any, bs: BinderPairs) = that match {
2478+
case that: AndOrType => isAnd == that.isAnd && tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs)
2479+
case _ => false
2480+
}
24532481
}
24542482

2455-
abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
2483+
abstract case class AndType(tp1: Type, tp2: Type) extends AndOrType {
24562484

24572485
def isAnd = true
24582486

@@ -2466,18 +2494,6 @@ object Types {
24662494

24672495
def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
24682496
derivedAndType(tp1, tp2)
2469-
2470-
override def computeHash(bs: Binders) = doHash(bs, tp1, tp2)
2471-
2472-
override def eql(that: Type) = that match {
2473-
case that: AndType => tp1.eq(that.tp1) && tp2.eq(that.tp2)
2474-
case _ => false
2475-
}
2476-
2477-
override def iso(that: Any, bs: BinderPairs) = that match {
2478-
case that: AndType => tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs)
2479-
case _ => false
2480-
}
24812497
}
24822498

24832499
final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2)
@@ -2506,7 +2522,7 @@ object Types {
25062522
if (checkValid) apply(tp1, tp2) else unchecked(tp1, tp2)
25072523
}
25082524

2509-
abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
2525+
abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType {
25102526

25112527
assert(tp1.isInstanceOf[ValueTypeOrWildcard] &&
25122528
tp2.isInstanceOf[ValueTypeOrWildcard], s"$tp1 $tp2")
@@ -2532,18 +2548,6 @@ object Types {
25322548

25332549
def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
25342550
derivedOrType(tp1, tp2)
2535-
2536-
override def computeHash(bs: Binders) = doHash(bs, tp1, tp2)
2537-
2538-
override def eql(that: Type) = that match {
2539-
case that: OrType => tp1.eq(that.tp1) && tp2.eq(that.tp2)
2540-
case _ => false
2541-
}
2542-
2543-
override def iso(that: Any, bs: BinderPairs) = that match {
2544-
case that: OrType => tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs)
2545-
case _ => false
2546-
}
25472551
}
25482552

25492553
final class CachedOrType(tp1: Type, tp2: Type) extends OrType(tp1, tp2)
@@ -2605,6 +2609,7 @@ object Types {
26052609
if (resType eq this.resType) this else ExprType(resType)
26062610

26072611
override def computeHash(bs: Binders) = doHash(bs, resType)
2612+
override def stableHash = resType.stableHash
26082613

26092614
override def eql(that: Type) = that match {
26102615
case that: ExprType => resType.eq(that.resType)
@@ -2693,10 +2698,11 @@ object Types {
26932698
abstract class HKLambda extends CachedProxyType with LambdaType {
26942699
final override def underlying(implicit ctx: Context) = resType
26952700

2696-
final override def computeHash(bs: Binders) =
2701+
override def computeHash(bs: Binders) =
26972702
doHash(new Binders(this, bs), paramNames, resType, paramInfos)
2703+
override def stableHash = resType.stableHash && paramInfos.stableHash
26982704

2699-
final override def eql(that: Type) = that match {
2705+
override def eql(that: Type) = that match {
27002706
case that: HKLambda =>
27012707
paramNames.equals(that.paramNames) &&
27022708
paramInfos.equals(that.paramInfos) &&
@@ -3128,6 +3134,7 @@ object Types {
31283134

31293135
private[this] var validSuper: Period = Nowhere
31303136
private[this] var cachedSuper: Type = _
3137+
private[this] var myStableHash: Byte = 0
31313138

31323139
override def underlying(implicit ctx: Context): Type = tycon
31333140

@@ -3173,6 +3180,12 @@ object Types {
31733180
else tycon.appliedTo(args)
31743181

31753182
override def computeHash(bs: Binders) = doHash(bs, tycon, args)
3183+
3184+
override def stableHash = {
3185+
if (myStableHash == 0) myStableHash = if (tycon.stableHash && args.stableHash) 1 else -1
3186+
myStableHash > 0
3187+
}
3188+
31763189
override def eql(that: Type) = this `eq` that // safe because applied types are hash-consed separately
31773190

31783191
final override def iso(that: Any, bs: BinderPairs) = that match {
@@ -3198,6 +3211,7 @@ object Types {
31983211
type BT <: Type
31993212
val binder: BT
32003213
def copyBoundType(bt: BT): Type
3214+
override def stableHash = false
32013215
}
32023216

32033217
abstract class ParamRef extends BoundType {
@@ -3469,6 +3483,7 @@ object Types {
34693483
else ClassInfo(prefix, cls, classParents, decls, selfInfo)
34703484

34713485
override def computeHash(bs: Binders) = doHash(bs, cls, prefix)
3486+
override def stableHash = prefix.stableHash && classParents.stableHash
34723487

34733488
override def eql(that: Type) = that match {
34743489
case that: ClassInfo =>
@@ -3562,6 +3577,7 @@ object Types {
35623577
}
35633578

35643579
override def computeHash(bs: Binders) = doHash(bs, lo, hi)
3580+
override def stableHash = lo.stableHash && hi.stableHash
35653581

35663582
override def iso(that: Any, bs: BinderPairs): Boolean = that match {
35673583
case that: TypeAlias => false
@@ -3585,6 +3601,7 @@ object Types {
35853601
if (alias eq this.alias) this else TypeAlias(alias)
35863602

35873603
override def computeHash(bs: Binders) = doHash(bs, alias)
3604+
override def stableHash = alias.stableHash
35883605

35893606
override def iso(that: Any, bs: BinderPairs): Boolean = that match {
35903607
case that: TypeAlias => alias.equals(that.alias, bs)
@@ -3649,6 +3666,7 @@ object Types {
36493666
if (elemtp eq this.elemType) this else JavaArrayType(elemtp)
36503667

36513668
override def computeHash(bs: Binders) = doHash(bs, elemType)
3669+
override def stableHash = elemType.stableHash
36523670

36533671
override def eql(that: Type) = that match {
36543672
case that: JavaArrayType => elemType.eq(that.elemType)
@@ -3717,6 +3735,7 @@ object Types {
37173735
else WildcardType(optBounds.asInstanceOf[TypeBounds])
37183736

37193737
override def computeHash(bs: Binders) = doHash(bs, optBounds)
3738+
override def stableHash = optBounds.stableHash
37203739

37213740
override def eql(that: Type) = that match {
37223741
case that: WildcardType => optBounds.eq(that.optBounds)
@@ -4510,7 +4529,9 @@ object Types {
45104529

45114530
implicit def decorateTypeApplications(tpe: Type): TypeApplications = new TypeApplications(tpe)
45124531

4513-
implicit class listEquals(val tps1: List[Type]) extends AnyVal {
4532+
implicit class typeListDeco(val tps1: List[Type]) extends AnyVal {
4533+
@tailrec def stableHash: Boolean =
4534+
tps1.isEmpty || tps1.head.stableHash && tps1.tail.stableHash
45144535
@tailrec def equalElements(tps2: List[Type], bs: BinderPairs): Boolean =
45154536
(tps1 `eq` tps2) || {
45164537
if (tps1.isEmpty) tps2.isEmpty

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ object ProtoTypes {
137137
val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0)
138138
addDelta(doHash(bs, name, memberProto), delta)
139139
}
140+
override def stableHash = memberProto.stableHash
140141
}
141142

142143
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
@@ -330,6 +331,7 @@ object ProtoTypes {
330331

331332
class CachedViewProto(argType: Type, resultType: Type) extends ViewProto(argType, resultType) {
332333
override def computeHash(bs: Hashable.Binders) = doHash(bs, argType, resultType)
334+
override def stableHash = argType.stableHash && resultType.stableHash
333335
}
334336

335337
object ViewProto {

0 commit comments

Comments
 (0)