aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/Uniques.scala
blob: 765b5d73fe58fc5689fa743431bf8114e46404c9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package dotty.tools.dotc
package core

import Types._, Contexts._, util.Stats._, Hashable._, Names._
import util.HashSet

/** Defines operation `unique` for hash-consing types.
 *  Also defines specialized hash sets for hash consing uniques of a specific type.
 *  All sets offer a `enterIfNew` method which checks whether a type
 *  with the given parts exists already and creates a new one if not.
 */
object Uniques {

  private def recordCaching(tp: Type): Unit = recordCaching(tp.hash, tp.getClass)
  private def recordCaching(h: Int, clazz: Class[_]): Unit =
    if (h == NotCached) {
      record("uncached-types")
      record(s"uncached: $clazz")
    } else {
      record("cached-types")
      record(s"cached: $clazz")
    }

  def unique[T <: Type](tp: T)(implicit ctx: Context): T = {
    if (monitored) recordCaching(tp)
    if (tp.hash == NotCached) tp
    else ctx.uniques.findEntryOrUpdate(tp).asInstanceOf[T]
  } /* !!! DEBUG
  ensuring (
    result => tp.toString == result.toString || {
      println(s"cache mismatch; tp = $tp, cached = $result")
      false
    }
  )
 */

  final class NamedTypeUniques extends HashSet[NamedType]("uniqueNamedTypes", initialUniquesCapacity) with Hashable {
    override def hash(x: NamedType): Int = x.hash

    private def findPrevious(h: Int, prefix: Type, name: Name): NamedType = {
      var e = findEntryByHash(h)
      while (e != null) {
        if ((e.prefix == prefix) && (e.name eq name)) return e
        e = nextEntryByHash(h)
      }
      e
    }

    def enterIfNew(prefix: Type, name: Name): NamedType = {
      val h = doHash(name, prefix)
      if (monitored) recordCaching(h, classOf[CachedTermRef])
      def newType =
        if (name.isTypeName) new CachedTypeRef(prefix, name.asTypeName, h)
        else new CachedTermRef(prefix, name.asTermName, h)
      if (h == NotCached) newType
      else {
        val r = findPrevious(h, prefix, name)
        if (r ne null) r else addEntryAfterScan(newType)
      }
    }
  }

  final class TypeBoundsUniques extends HashSet[TypeBounds]("uniqueTypeBounds", initialUniquesCapacity) with Hashable {
    override def hash(x: TypeBounds): Int = x.hash

    private def findPrevious(h: Int, lo: Type, hi: Type, variance: Int): TypeBounds = {
      var e = findEntryByHash(h)
      while (e != null) {
        if ((e.lo == lo) && (e.hi == hi) && (e.variance == variance)) return e
        e = nextEntryByHash(h)
      }
      e
    }

    def enterIfNew(lo: Type, hi: Type, variance: Int): TypeBounds = {
      val h = doHash(variance, lo, hi)
      if (monitored) recordCaching(h, classOf[TypeBounds])
      def newBounds =
        if (variance == 0) new CachedTypeBounds(lo, hi, h)
        else if (variance == 1) new CoTypeBounds(lo, hi, h)
        else new ContraTypeBounds(lo, hi, h)
      if (h == NotCached) newBounds
      else {
        val r = findPrevious(h, lo, hi, variance)
        if (r ne null) r else addEntryAfterScan(newBounds)
      }
    }
  }

  final class RefinedUniques extends HashSet[RefinedType]("uniqueRefinedTypes", initialUniquesCapacity) with Hashable {
    override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed
    override def hash(x: RefinedType): Int = x.hash

    private def findPrevious(h: Int, parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = {
      var e = findEntryByHash(h)
      while (e != null) {
        if ((e.parent == parent) && (e.refinedName eq refinedName) && (e.refinedInfo == refinedInfo))
          return e
        e = nextEntryByHash(h)
      }
      e
    }

    def enterIfNew(parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = {
      val h = doHash(refinedName, refinedInfo, parent)
      def newType = new PreHashedRefinedType(parent, refinedName, refinedInfo, h)
      if (monitored) recordCaching(h, classOf[PreHashedRefinedType])
      if (h == NotCached) newType
      else {
        val r = findPrevious(h, parent, refinedName, refinedInfo)
        if (r ne null) r else addEntryAfterScan(newType)
      }
    }

    def enterIfNew(rt: RefinedType) = {
      if (monitored) recordCaching(rt)
      if (rt.hash == NotCached) rt
      else {
        val r = findPrevious(rt.hash, rt.parent, rt.refinedName, rt.refinedInfo)
        if (r ne null) r else addEntryAfterScan(rt)
      }
    }
  }
}