Skip to content

Commit 03377c7

Browse files
committed
Structural hashing and equality for dependent types.
Dependent types RecType and HKLambda were generative. Any two such types were considered to be different, even if they had the same structure. This causes problems for subtyping and constraint solving. For instance, we cannot detect that a Hk-Lambda has already been added to a constraint, which can cause a cycle. Also, monitoredIsSubtype would not work if the compared types are dependent. Test cases are i3695.scala and i3965a.scala. To fix this, we need to have a notion of hashing and equality which identifies isomorphic HkLambdas and RecTypes. Since these are very frequently called operations, a lot of attention to detail is needed in order not to lose performance.
1 parent e9388bc commit 03377c7

File tree

4 files changed

+207
-64
lines changed

4 files changed

+207
-64
lines changed

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ package core
33

44
import Types._
55
import scala.util.hashing.{ MurmurHash3 => hashing }
6+
import annotation.tailrec
67

78
object Hashable {
8-
9-
type Binders = Array[BindingType]
9+
10+
/** A null terminated list of BindingTypes. We use `null` here for efficiency */
11+
class Binders(val tp: BindingType, val next: Binders)
12+
13+
/** A null terminated list of pairs of BindingTypes. Used for isomorphism tests. */
14+
class BinderPairs(tp1: BindingType, tp2: BindingType, next: BinderPairs) {
15+
@tailrec final def matches(t1: Type, t2: Type): Boolean =
16+
(t1 `eq` tp1) && (t2 `eq` tp2) || next != null && next.matches(t1, t2)
17+
}
1018

1119
/** A hash value indicating that the underlying type is not
1220
* cached in uniques.
@@ -36,7 +44,7 @@ trait Hashable {
3644
avoidSpecialHashes(hashing.finalizeHash(hashCode, arity))
3745

3846
final def typeHash(bs: Binders, tp: Type) =
39-
if (bs == null) tp.hash else tp.computeHash(bs)
47+
if (bs == null || tp.stableHash) tp.hash else tp.computeHash(bs)
4048

4149
def identityHash(bs: Binders) = avoidSpecialHashes(System.identityHashCode(this))
4250

@@ -72,6 +80,7 @@ trait Hashable {
7280
finishHash(bs, hashing.mix(seed, elemHash), arity + 1, tps)
7381
}
7482

83+
7584
protected final def doHash(x: Any): Int =
7685
finishHash(hashing.mix(hashSeed, x.hashCode), 1)
7786

@@ -93,15 +102,14 @@ trait Hashable {
93102
protected final def doHash(bs: Binders, x1: Any, tp2: Type, tps3: List[Type]): Int =
94103
finishHash(bs, hashing.mix(hashSeed, x1.hashCode), 1, tp2, tps3)
95104

96-
97-
protected final def doHash(bs: Binders, x1: Int, x2: Int): Int =
105+
protected final def doHash(x1: Int, x2: Int): Int =
98106
finishHash(hashing.mix(hashing.mix(hashSeed, x1), x2), 1)
99107

100108
protected final def addDelta(elemHash: Int, delta: Int) =
101109
if (elemHash == NotCached) NotCached
102110
else avoidSpecialHashes(elemHash + delta)
103111

104-
private def avoidSpecialHashes(h: Int) =
112+
protected def avoidSpecialHashes(h: Int) =
105113
if (h == NotCached) NotCachedAlt
106114
else if (h == HashUnknown) HashUnknownAlt
107115
else h

0 commit comments

Comments
 (0)