aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Uniques.scala
blob: cb9670c69956f8e48ef9c8b8a0b2c00b9d631ee6 (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
125
126
127
128
package dotty.tools.dotc
package core

import Types._, Contexts._, util.Stats._, Hashable._, Names._
import config.Config
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 if (monitored) {
      val size = ctx.uniques.size
      val result = ctx.uniques.findEntryOrUpdate(tp).asInstanceOf[T]
      if (ctx.uniques.size > size) record(s"fresh unique ${tp.getClass}")
      result
    } 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](Config.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 eq 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 TypeAliasUniques extends HashSet[TypeAlias](Config.initialUniquesCapacity) with Hashable {
    override def hash(x: TypeAlias): Int = x.hash

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

    def enterIfNew(alias: Type, variance: Int): TypeAlias = {
      val h = doHash(variance, alias)
      if (monitored) recordCaching(h, classOf[TypeAlias])
      def newAlias = new CachedTypeAlias(alias, variance, h)
      if (h == NotCached) newAlias
      else {
        val r = findPrevious(h, alias, variance)
        if (r ne null) r
        else addEntryAfterScan(newAlias)
      }
    }
  }

  final class RefinedUniques extends HashSet[RefinedType](Config.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 eq parent) && (e.refinedName eq refinedName) && (e.refinedInfo eq 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 CachedRefinedType(parent, refinedName, refinedInfo, h)
      if (monitored) recordCaching(h, classOf[CachedRefinedType])
      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)
      }
    }
  }
}