@@ -1621,126 +1621,140 @@ object SymDenotations {
16211621
16221622 /** Compute tp.baseType(this) */
16231623 final def baseTypeOf (tp : Type )(implicit ctx : Context ): Type = {
1624+ val btrCache = baseTypeCache
1625+ def inCache (tp : Type ) = btrCache.get(tp) != null
16241626
1625- def foldGlb ( bt : Type , ps : List [ Type ]) : Type = ps match {
1626- case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1 )
1627- case _ => bt
1627+ def ensureAcyclic ( baseTp : Type ) = {
1628+ if (baseTp `eq` NoPrefix ) throw CyclicReference ( this )
1629+ baseTp
16281630 }
16291631
1630- /** We cannot cache:
1631- * - type variables which are uninstantiated or whose instances can
1632- * change, depending on typerstate.
1633- * - types where the underlying type is an ErasedValueType, because
1634- * this underlying type will change after ElimErasedValueType,
1635- * and this changes subtyping relations. As a shortcut, we do not
1636- * cache ErasedValueType at all.
1637- */
1638- def isCachable (tp : Type , btrCache : BaseTypeMap ): Boolean = {
1639- def inCache (tp : Type ) = btrCache.containsKey(tp) && isCachable(tp, btrCache)
1632+ def recur (tp : Type ): Type = try {
16401633 tp match {
1641- case tp : TypeRef if tp.symbol.isClass => true
1642- case tp : TypeVar => tp.inst.exists && inCache(tp.inst)
1643- case tp : TypeParamRef if ctx.typerState.constraint.contains(tp) => false
1644- // case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing
1645- case tp : TypeProxy => isCachable(tp.underlying, btrCache)
1646- case tp : AndType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache)
1647- case tp : OrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache)
1648- case _ : TypeErasure .ErasedValueType => false
1649- case _ => true
1634+ case tp : CachedType =>
1635+ val baseTp = btrCache.get(tp)
1636+ if (baseTp != null ) return ensureAcyclic(baseTp)
1637+ case _ =>
16501638 }
1651- }
1652-
1653- def computeBaseTypeOf (tp : Type ): Type = {
16541639 if (Stats .monitored) {
16551640 Stats .record(" computeBaseType, total" )
16561641 Stats .record(s " computeBaseType, ${tp.getClass}" )
16571642 }
1658- if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
1659- symbol.typeRef
1660- else tp match {
1643+ tp match {
16611644 case tp @ TypeRef (prefix, _) =>
1662- val subsym = tp.symbol
1663- if (subsym eq symbol) tp
1664- else subsym.denot match {
1665- case clsd : ClassDenotation =>
1666- val owner = clsd.owner
1667- val isOwnThis = prefix match {
1668- case prefix : ThisType => prefix.cls eq owner
1669- case NoPrefix => true
1670- case _ => false
1671- }
1672- if (isOwnThis)
1673- if (clsd.baseClassSet.contains(symbol)) foldGlb(NoType , clsd.classParents)
1674- else NoType
1675- else
1676- baseTypeOf(clsd.typeRef).asSeenFrom(prefix, owner)
1677- case _ =>
1678- baseTypeOf(tp.superType)
1645+
1646+ def foldGlb (bt : Type , ps : List [Type ]): Type = ps match {
1647+ case p :: ps1 => foldGlb(bt & recur(p), ps1)
1648+ case _ => bt
16791649 }
1650+
1651+ def computeTypeRef = {
1652+ btrCache.put(tp, NoPrefix )
1653+ tp.symbol.denot match {
1654+ case clsd : ClassDenotation =>
1655+ def isOwnThis = prefix match {
1656+ case prefix : ThisType => prefix.cls `eq` clsd.owner
1657+ case NoPrefix => true
1658+ case _ => false
1659+ }
1660+ val baseTp =
1661+ if (tp.symbol eq symbol)
1662+ tp
1663+ else if (isOwnThis)
1664+ if (clsd.baseClassSet.contains(symbol))
1665+ if (symbol.isStatic && symbol.typeParams.isEmpty) symbol.typeRef
1666+ else foldGlb(NoType , clsd.classParents)
1667+ else NoType
1668+ else
1669+ recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner)
1670+ if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp)
1671+ baseTp
1672+ case _ =>
1673+ val superTp = tp.superType
1674+ val baseTp = recur(superTp)
1675+ if (baseTp.exists && inCache(superTp) && tp.symbol.maybeOwner.isType)
1676+ btrCache.put(tp, baseTp) // typeref cannot be a GADT, so cache is stable
1677+ else
1678+ btrCache.remove(tp)
1679+ baseTp
1680+ }
1681+ }
1682+ computeTypeRef
1683+
16801684 case tp @ AppliedType (tycon, args) =>
1681- val subsym = tycon.typeSymbol
1682- if (subsym eq symbol) tp
1683- else (tycon.typeParams: @ unchecked) match {
1684- case LambdaParam (_, _) :: _ =>
1685- baseTypeOf(tp.superType)
1686- case tparams : List [Symbol @ unchecked] =>
1687- baseTypeOf(tycon).subst(tparams, args)
1685+
1686+ def computeApplied = {
1687+ btrCache.put(tp, NoPrefix )
1688+ val baseTp =
1689+ if (tycon.typeSymbol eq symbol) tp
1690+ else (tycon.typeParams: @ unchecked) match {
1691+ case LambdaParam (_, _) :: _ =>
1692+ recur(tp.superType)
1693+ case tparams : List [Symbol @ unchecked] =>
1694+ recur(tycon).subst(tparams, args)
1695+ }
1696+ if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp)
1697+ baseTp
16881698 }
1689- case tp : TypeParamRef =>
1690- baseTypeOf(ctx.typeComparer.bounds(tp).hi)
1699+ computeApplied
1700+
1701+ case tp : TypeParamRef => // uncachable, since baseType depends on context bounds
1702+ recur(ctx.typeComparer.bounds(tp).hi)
16911703 case tp : TypeProxy =>
1692- baseTypeOf(tp.superType)
1693- case AndType (tp1, tp2) =>
1694- baseTypeOf(tp1) & baseTypeOf(tp2) match {
1695- case AndType (tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1696- case res => res
1704+
1705+ def computeTypeProxy = {
1706+ val superTp = tp.superType
1707+ val baseTp = recur(superTp)
1708+ tp match {
1709+ case tp : CachedType if baseTp.exists && inCache(superTp) =>
1710+ // Note: This also works for TypeVars: If they are not instantiated, their supertype
1711+ // is a TypeParamRef, which is never cached. So uninstantiated TypeVars are not cached either.
1712+ btrCache.put(tp, baseTp)
1713+ case _ =>
1714+ }
1715+ baseTp
16971716 }
1698- case OrType (tp1, tp2) =>
1699- baseTypeOf(tp1) | baseTypeOf(tp2) match {
1700- case OrType (tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1701- case res => res
1717+ computeTypeProxy
1718+
1719+ case tp : AndOrType =>
1720+
1721+ def computeAndOrType = {
1722+ val tp1 = tp.tp1
1723+ val tp2 = tp.tp2
1724+ val baseTp =
1725+ if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
1726+ symbol.typeRef
1727+ else {
1728+ val baseTp1 = recur(tp1)
1729+ val baseTp2 = recur(tp2)
1730+ val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2
1731+ combined match {
1732+ case combined : AndOrType
1733+ if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp
1734+ case _ => combined
1735+ }
1736+ }
1737+ if (baseTp.exists && inCache(tp1) && inCache(tp2)) btrCache.put(tp, baseTp)
1738+ baseTp
17021739 }
1740+ computeAndOrType
1741+
17031742 case JavaArrayType (_) if symbol == defn.ObjectClass =>
17041743 this .typeRef
17051744 case _ =>
17061745 NoType
17071746 }
17081747 }
1748+ catch {
1749+ case ex : Throwable =>
1750+ btrCache.remove(tp)
1751+ throw ex
1752+ }
1753+
17091754
17101755 /* >|>*/ trace.onDebug(s " $tp.baseType( $this) " ) /* <|<*/ {
17111756 Stats .record(" baseTypeOf" )
1712- tp.stripTypeVar match {
1713- case tp : CachedType =>
1714- val btrCache = baseTypeCache
1715- if (! isCachable(tp, btrCache))
1716- computeBaseTypeOf(tp)
1717- else
1718- try {
1719- var basetp = btrCache.get(tp)
1720- if (basetp == null ) {
1721- btrCache.put(tp, NoPrefix )
1722- basetp = computeBaseTypeOf(tp)
1723- if (basetp.exists) {
1724- Stats .record(" cached base type exists" )
1725- btrCache.put(tp, basetp)
1726- }
1727- else {
1728- Stats .record(" cached base type missing" )
1729- btrCache.remove(tp)
1730- }
1731- }
1732- else if (basetp `eq` NoPrefix )
1733- throw CyclicReference (this )
1734- basetp
1735- }
1736- catch {
1737- case ex : Throwable =>
1738- btrCache.put(tp, null )
1739- throw ex
1740- }
1741- case tp =>
1742- computeBaseTypeOf(tp)
1743- }
1757+ recur(tp)
17441758 }
17451759 }
17461760
0 commit comments