@@ -1620,118 +1620,147 @@ object SymDenotations {
1620
1620
1621
1621
/** Compute tp.baseType(this) */
1622
1622
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
+ }
1623
1632
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
1627
1636
}
1628
1637
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 {
1639
1639
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 _ =>
1648
1644
}
1649
- }
1650
-
1651
- def computeBaseTypeOf (tp : Type ): Type = {
1652
1645
if (Stats .monitored) {
1653
1646
Stats .record(" computeBaseType, total" )
1654
1647
Stats .record(s " computeBaseType, ${tp.getClass}" )
1655
1648
}
1656
- if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
1657
- symbol.typeRef
1658
- else tp match {
1649
+ tp match {
1659
1650
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
1677
1655
}
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
+
1678
1690
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
1686
1704
}
1705
+ computeApplied
1706
+
1707
+ case tp : TypeParamRef => // uncachable, since baseType depends on context bounds
1708
+ recur(ctx.typeComparer.bounds(tp).hi)
1687
1709
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
1693
1722
}
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
1698
1745
}
1746
+ computeAndOrType
1747
+
1699
1748
case JavaArrayType (_) if symbol == defn.ObjectClass =>
1700
1749
this .typeRef
1701
1750
case _ =>
1702
1751
NoType
1703
1752
}
1704
1753
}
1754
+ catch {
1755
+ case ex : Throwable =>
1756
+ btrCache.remove(tp)
1757
+ throw ex
1758
+ }
1759
+
1705
1760
1706
1761
/* >|>*/ trace.onDebug(s " $tp.baseType( $this) " ) /* <|<*/ {
1707
1762
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)
1735
1764
}
1736
1765
}
1737
1766
0 commit comments