aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/core/Uniques.scala
blob: 765b5d73fe58fc5689fa743431bf8114e46404c9 (plain) (tree)



























































































































                                                                                                                                           
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)
      }
    }
  }
}