From 15cb1c75355f0d38da5281d165edfb2fd1a3b049 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 1 Jan 2011 07:51:33 +0000 Subject: Generalized a pile of duplicated type caching c... Generalized a pile of duplicated type caching code, but it's still largely undeployed because I haven't figured out how to make it as fast as the copy-pasted version, which bugs me like you wouldn't believe. Cannot deal with making "Sophie's Choice" between performance and lack of duplication. I left one deployment in because it fixed a bug, or at least so I see it: can't see why one would want the two absent error messages in t2641.check. Review by odersky. --- src/compiler/scala/tools/nsc/symtab/Caches.scala | 103 +++++++++++++++++++++ .../scala/tools/nsc/symtab/SymbolTable.scala | 1 + src/compiler/scala/tools/nsc/symtab/Symbols.scala | 11 ++- src/compiler/scala/tools/nsc/symtab/Types.scala | 39 +------- 4 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/symtab/Caches.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/symtab/Caches.scala b/src/compiler/scala/tools/nsc/symtab/Caches.scala new file mode 100644 index 0000000000..f47da9c84c --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Caches.scala @@ -0,0 +1,103 @@ +/* NSC -- new scala compiler + * Copyright 2005-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package symtab + +import scala.collection.{ mutable, immutable } + +/** A cache for some entity whose validity depends on a monotonically + * increasing sequence number. + */ +abstract class SequencedCache[T >: Null] { + def zero: Int + def sequenceId: Int + def calculate(): T + + /** If sequence numbers differ, this condition is consulted before + * updating the cached value. + */ + def isCacheValid: Boolean + + /** Public so accesses can be inlined. */ + @inline var cachedId: Int = 0 + @inline var cachedValue: T = _ + + /** Puts cache back in uninitialized state. */ + @inline final def clear() = { + cachedId = zero + cachedValue = null + } + /** Resets the sequence id without touching the cached value. */ + @inline final def reset() = { + cachedId = zero + } + + final def get(): T = { + if (cachedValue == null) { + cachedValue = calculate() + cachedId = sequenceId + } + else if (cachedId != sequenceId) { + if (!isCacheValid) + cachedValue = calculate() + + cachedId = sequenceId + } + cachedValue + } +} + +trait Caches { + self: SymbolTable => + + final def isValid(period: Period): Boolean = + period != 0 && runId(period) == currentRunId && { + val pid = phaseId(period) + if (phase.id > pid) infoTransformers.nextFrom(pid).pid >= phase.id + else infoTransformers.nextFrom(phase.id).pid >= pid + } + + final def isValidForBaseClasses(period: Period): Boolean = { + def noChangeInBaseClasses(it: InfoTransformer, limit: Phase#Id): Boolean = ( + it.pid >= limit || + !it.changesBaseClasses && noChangeInBaseClasses(it.next, limit) + ); + period != 0 && runId(period) == currentRunId && { + val pid = phaseId(period) + if (phase.id > pid) noChangeInBaseClasses(infoTransformers.nextFrom(pid), phase.id) + else noChangeInBaseClasses(infoTransformers.nextFrom(phase.id), pid) + } + } + + abstract class PeriodCache[T >: Null] extends SequencedCache[T] { + final val zero = NoPeriod + @inline final def sequenceId = currentPeriod + } + + abstract class ListOfTypesCache extends PeriodCache[List[Type]] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class ListOfSymbolsCache extends PeriodCache[List[Symbol]] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class BaseTypeSeqCache extends PeriodCache[BaseTypeSeq] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class TypeCache extends PeriodCache[Type] { + @inline final def isCacheValid = isValid(cachedId) + } + abstract class TypeCacheForRunId extends SequencedCache[Type] { + final val zero = NoRunId + @inline final def sequenceId = currentRunId + @inline final override def isCacheValid = false + } + object TypeCache { + def apply(body: => Type): TypeCache = new TypeCache { + @inline final def calculate() = body + } + } +} + diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index 03166de05d..97b7ce03bf 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -15,6 +15,7 @@ abstract class SymbolTable extends reflect.generic.Universe with Symbols with Types with Scopes + with Caches with Definitions with reflect.generic.Constants with BaseTypeSeqs diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index feccb4e462..43dc2b768b 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -1801,6 +1801,11 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => override def isAbstractType = isDeferred override def isAliasType = !isDeferred + private def newTypeRef(targs: List[Type]) = { + val pre = if (hasFlag(PARAM | EXISTENTIAL)) NoPrefix else owner.thisType + typeRef(pre, this, targs) + } + /** Let's say you have a type definition * * type T <: Number @@ -1823,8 +1828,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => else unsafeTypeParams map (_.typeConstructor) //@M! use typeConstructor to generate dummy type arguments, // sym.tpe should not be called on a symbol that's supposed to be a higher-kinded type // memberType should be used instead, that's why it uses tpeHK and not tpe - tpeCache = typeRef(if (hasFlag(PARAM | EXISTENTIAL)) NoPrefix else owner.thisType, - this, targs) + tpeCache = newTypeRef(targs) } } assert(tpeCache ne null/*, "" + this + " " + phase*/)//debug @@ -1836,8 +1840,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => override def typeConstructor: Type = { if ((tyconCache eq null) || tyconRunId != currentRunId) { - tyconCache = typeRef(if (hasFlag(PARAM | EXISTENTIAL)) NoPrefix else owner.thisType, - this, List()) + tyconCache = newTypeRef(Nil) tyconRunId = currentRunId } assert(tyconCache ne null) diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 28e666755e..508274f73f 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -1648,8 +1648,10 @@ trait Types extends reflect.generic.Types { self: SymbolTable => // assert(!(sym hasFlag (PARAM | EXISTENTIAL)) || pre == NoPrefix, this) // assert(args.isEmpty || !sym.info.typeParams.isEmpty, this) // assert(args.isEmpty || ((sym ne AnyClass) && (sym ne NothingClass)) - private var parentsCache: List[Type] = _ - private var parentsPeriod = NoPeriod + + private val parentsCache = new ListOfTypesCache { + @inline final def calculate() = thisInfo.parents map transform + } private var baseTypeSeqCache: BaseTypeSeq = _ private var baseTypeSeqPeriod = NoPeriod @@ -1729,19 +1731,7 @@ A type's typeSymbol should never be inspected directly. if (sym.isAbstractType) thisInfo.bounds // transform(thisInfo.bounds).asInstanceOf[TypeBounds] // ??? seems to be doing asSeenFrom twice else super.bounds - override def parents: List[Type] = { - val period = parentsPeriod - if (period != currentPeriod) { - parentsPeriod = currentPeriod - if (!isValidForBaseClasses(period)) { - parentsCache = thisInfo.parents map transform - } else if (parentsCache == null) { // seems this can happen if things are currupted enough, see #2641 - parentsCache = List(AnyClass.tpe) - } - } - parentsCache - } - + override def parents: List[Type] = parentsCache.get() override def typeOfThis = transform(sym.typeOfThis) /* @@ -3912,25 +3902,6 @@ A type's typeSymbol should never be inspected directly. d + LubGlbMargin } - final def isValid(period: Period): Boolean = - period != 0 && runId(period) == currentRunId && { - val pid = phaseId(period) - if (phase.id > pid) infoTransformers.nextFrom(pid).pid >= phase.id - else infoTransformers.nextFrom(phase.id).pid >= pid - } - - final def isValidForBaseClasses(period: Period): Boolean = { - def noChangeInBaseClasses(it: InfoTransformer, limit: Phase#Id): Boolean = ( - it.pid >= limit || - !it.changesBaseClasses && noChangeInBaseClasses(it.next, limit) - ); - period != 0 && runId(period) == currentRunId && { - val pid = phaseId(period) - if (phase.id > pid) noChangeInBaseClasses(infoTransformers.nextFrom(pid), phase.id) - else noChangeInBaseClasses(infoTransformers.nextFrom(phase.id), pid) - } - } - /** Is intersection of given types populated? That is, * for all types tp1, tp2 in intersection * for all common base classes bc of tp1 and tp2 -- cgit v1.2.3