@@ -1620,118 +1620,147 @@ object SymDenotations {
16201620
16211621 /** Compute tp.baseType(this) */
16221622 final def baseTypeOf (tp : Type )(implicit ctx : Context ): Type = {
1623+ val btrCache = baseTypeCache
1624+ def inCache (tp : Type ) = btrCache.get(tp) != null
1625+ def record (tp : CachedType , baseTp : Type ) = {
1626+ if (Stats .monitored) {
1627+ Stats .record(" basetype cache entries" )
1628+ if (! baseTp.exists) Stats .record(" basetype cache NoTypes" )
1629+ }
1630+ btrCache.put(tp, baseTp)
1631+ }
16231632
1624- def foldGlb ( bt : Type , ps : List [ Type ]) : Type = ps match {
1625- case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1 )
1626- case _ => bt
1633+ def ensureAcyclic ( baseTp : Type ) = {
1634+ if (baseTp `eq` NoPrefix ) throw CyclicReference ( this )
1635+ baseTp
16271636 }
16281637
1629- /** We cannot cache:
1630- * - type variables which are uninstantiated or whose instances can
1631- * change, depending on typerstate.
1632- * - types where the underlying type is an ErasedValueType, because
1633- * this underlying type will change after ElimErasedValueType,
1634- * and this changes subtyping relations. As a shortcut, we do not
1635- * cache ErasedValueType at all.
1636- */
1637- def isCachable (tp : Type , btrCache : BaseTypeMap ): Boolean = {
1638- def inCache (tp : Type ) = btrCache.containsKey(tp)
1638+ def recur (tp : Type ): Type = try {
16391639 tp match {
1640- case _ : TypeErasure .ErasedValueType => false
1641- case tp : TypeRef if tp.symbol.isClass => true
1642- case tp : TypeVar => tp.inst.exists && inCache(tp.inst)
1643- // case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing
1644- case tp : TypeProxy => isCachable(tp.underlying, btrCache)
1645- case tp : AndType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache)
1646- case tp : OrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache)
1647- case _ => true
1640+ case tp : CachedType =>
1641+ val baseTp = btrCache.get(tp)
1642+ if (baseTp != null ) return ensureAcyclic(baseTp)
1643+ case _ =>
16481644 }
1649- }
1650-
1651- def computeBaseTypeOf (tp : Type ): Type = {
16521645 if (Stats .monitored) {
16531646 Stats .record(" computeBaseType, total" )
16541647 Stats .record(s " computeBaseType, ${tp.getClass}" )
16551648 }
1656- if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
1657- symbol.typeRef
1658- else tp match {
1649+ tp match {
16591650 case tp @ TypeRef (prefix, _) =>
1660- val subsym = tp.symbol
1661- if (subsym eq symbol) tp
1662- else subsym.denot match {
1663- case clsd : ClassDenotation =>
1664- val owner = clsd.owner
1665- val isOwnThis = prefix match {
1666- case prefix : ThisType => prefix.cls eq owner
1667- case NoPrefix => true
1668- case _ => false
1669- }
1670- if (isOwnThis)
1671- if (clsd.baseClassSet.contains(symbol)) foldGlb(NoType , clsd.classParents)
1672- else NoType
1673- else
1674- baseTypeOf(clsd.typeRef).asSeenFrom(prefix, owner)
1675- case _ =>
1676- baseTypeOf(tp.superType)
1651+
1652+ def foldGlb (bt : Type , ps : List [Type ]): Type = ps match {
1653+ case p :: ps1 => foldGlb(bt & recur(p), ps1)
1654+ case _ => bt
16771655 }
1656+
1657+ def computeTypeRef = {
1658+ btrCache.put(tp, NoPrefix )
1659+ tp.symbol.denot match {
1660+ case clsd : ClassDenotation =>
1661+ def isOwnThis = prefix match {
1662+ case prefix : ThisType => prefix.cls `eq` clsd.owner
1663+ case NoPrefix => true
1664+ case _ => false
1665+ }
1666+ val baseTp =
1667+ if (tp.symbol eq symbol)
1668+ tp
1669+ else if (isOwnThis)
1670+ if (clsd.baseClassSet.contains(symbol))
1671+ if (symbol.isStatic && symbol.typeParams.isEmpty) symbol.typeRef
1672+ else foldGlb(NoType , clsd.classParents)
1673+ else NoType
1674+ else
1675+ recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner)
1676+ record(tp, baseTp)
1677+ baseTp
1678+ case _ =>
1679+ val superTp = tp.superType
1680+ val baseTp = recur(superTp)
1681+ if (inCache(superTp) && tp.symbol.maybeOwner.isType)
1682+ record(tp, baseTp) // typeref cannot be a GADT, so cache is stable
1683+ else
1684+ btrCache.remove(tp)
1685+ baseTp
1686+ }
1687+ }
1688+ computeTypeRef
1689+
16781690 case tp @ AppliedType (tycon, args) =>
1679- val subsym = tycon.typeSymbol
1680- if (subsym eq symbol) tp
1681- else (tycon.typeParams: @ unchecked) match {
1682- case LambdaParam (_, _) :: _ =>
1683- baseTypeOf(tp.superType)
1684- case tparams : List [Symbol @ unchecked] =>
1685- baseTypeOf(tycon).subst(tparams, args)
1691+
1692+ def computeApplied = {
1693+ btrCache.put(tp, NoPrefix )
1694+ val baseTp =
1695+ if (tycon.typeSymbol eq symbol) tp
1696+ else (tycon.typeParams: @ unchecked) match {
1697+ case LambdaParam (_, _) :: _ =>
1698+ recur(tp.superType)
1699+ case tparams : List [Symbol @ unchecked] =>
1700+ recur(tycon).subst(tparams, args)
1701+ }
1702+ record(tp, baseTp)
1703+ baseTp
16861704 }
1705+ computeApplied
1706+
1707+ case tp : TypeParamRef => // uncachable, since baseType depends on context bounds
1708+ recur(ctx.typeComparer.bounds(tp).hi)
16871709 case tp : TypeProxy =>
1688- baseTypeOf(tp.superType)
1689- case AndType (tp1, tp2) =>
1690- baseTypeOf(tp1) & baseTypeOf(tp2) match {
1691- case AndType (tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1692- case res => res
1710+
1711+ def computeTypeProxy = {
1712+ val superTp = tp.superType
1713+ val baseTp = recur(superTp)
1714+ tp match {
1715+ case tp : CachedType if baseTp.exists && inCache(superTp) =>
1716+ // Note: This also works for TypeVars: If they are not instantiated, their supertype
1717+ // is a TypeParamRef, which is never cached. So uninstantiated TypeVars are not cached either.
1718+ record(tp, baseTp)
1719+ case _ =>
1720+ }
1721+ baseTp
16931722 }
1694- case OrType (tp1, tp2) =>
1695- baseTypeOf(tp1) | baseTypeOf(tp2) match {
1696- case OrType (tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1697- case res => res
1723+ computeTypeProxy
1724+
1725+ case tp : AndOrType =>
1726+
1727+ def computeAndOrType = {
1728+ val tp1 = tp.tp1
1729+ val tp2 = tp.tp2
1730+ val baseTp =
1731+ if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
1732+ symbol.typeRef
1733+ else {
1734+ val baseTp1 = recur(tp1)
1735+ val baseTp2 = recur(tp2)
1736+ val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2
1737+ combined match {
1738+ case combined : AndOrType
1739+ if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp
1740+ case _ => combined
1741+ }
1742+ }
1743+ if (baseTp.exists && inCache(tp1) && inCache(tp2)) record(tp, baseTp)
1744+ baseTp
16981745 }
1746+ computeAndOrType
1747+
16991748 case JavaArrayType (_) if symbol == defn.ObjectClass =>
17001749 this .typeRef
17011750 case _ =>
17021751 NoType
17031752 }
17041753 }
1754+ catch {
1755+ case ex : Throwable =>
1756+ btrCache.remove(tp)
1757+ throw ex
1758+ }
1759+
17051760
17061761 /* >|>*/ trace.onDebug(s " $tp.baseType( $this) " ) /* <|<*/ {
17071762 Stats .record(" baseTypeOf" )
1708- tp.stripTypeVar match {
1709- case tp : CachedType =>
1710- val btrCache = baseTypeCache
1711- try {
1712- var basetp = btrCache get tp
1713- if (basetp == null ) {
1714- btrCache.put(tp, NoPrefix )
1715- basetp = computeBaseTypeOf(tp)
1716- if (! basetp.exists) Stats .record(" base type miss" )
1717- if (isCachable(tp, btrCache)) {
1718- if (basetp.exists) Stats .record(" cached base type hit" )
1719- else Stats .record(" cached base type miss" )
1720- btrCache.put(tp, basetp)
1721- }
1722- else btrCache.remove(tp)
1723- } else if (basetp `eq` NoPrefix )
1724- throw CyclicReference (this )
1725- basetp
1726- }
1727- catch {
1728- case ex : Throwable =>
1729- btrCache.put(tp, null )
1730- throw ex
1731- }
1732- case tp =>
1733- computeBaseTypeOf(tp)
1734- }
1763+ recur(tp)
17351764 }
17361765 }
17371766
0 commit comments