Skip to content

Cache also private members #9648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 1, 2020
27 changes: 11 additions & 16 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ object Denotations {
final def first: SingleDenotation = this
final def last: SingleDenotation = this

final def matches(other: SingleDenotation)(using Context): Boolean =
def matches(other: SingleDenotation)(using Context): Boolean =
val d = signature.matchDegree(other.signature)

d match
Expand Down Expand Up @@ -1013,16 +1013,21 @@ object Denotations {
end matches

def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(using Context): SingleDenotation =
if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation
else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre)
if hasUniqueSym && prevDenots.containsSym(symbol) then NoDenotation
else if isType then filterDisjoint(ownDenots).asSeenFrom(pre)
else asSeenFrom(pre).filterDisjoint(ownDenots)

final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
if (p(this)) this else NoDenotation
final def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation =
def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation =
if (denots.exists && denots.matches(this)) NoDenotation else this
def filterWithFlags(required: FlagSet, excluded: FlagSet)(using Context): SingleDenotation =
if (required.isEmpty && excluded.isEmpty || compatibleWith(required, excluded)) this else NoDenotation
def symd: SymDenotation = this match
case symd: SymDenotation => symd
case _ => symbol.denot
if !required.isEmpty && !symd.isAllOf(required)
|| !excluded.isEmpty && symd.isOneOf(excluded) then NoDenotation
else this
def aggregate[T](f: SingleDenotation => T, g: (T, T) => T): T = f(this)

type AsSeenFromResult = SingleDenotation
Expand Down Expand Up @@ -1056,16 +1061,6 @@ object Denotations {
if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this
else derived(symbol.info)
}

/** Does this denotation have all the `required` flags but none of the `excluded` flags?
*/
private def compatibleWith(required: FlagSet, excluded: FlagSet)(using Context): Boolean = {
val symd: SymDenotation = this match {
case symd: SymDenotation => symd
case _ => symbol.denot
}
symd.isAllOf(required) && !symd.isOneOf(excluded)
}
}

abstract class NonSymSingleDenotation(symbol: Symbol, initInfo: Type, override val prefix: Type) extends SingleDenotation(symbol, initInfo) {
Expand Down
9 changes: 2 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,11 @@ object Scopes {
* Symbols occur in the result in reverse order relative to their occurrence
* in `this.toList`.
*/
final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(using Context): PreDenotation = {
final def denotsNamed(name: Name)(using Context): PreDenotation = {
var syms: PreDenotation = NoDenotation
var e = lookupEntry(name)
while (e != null) {
val d = e.sym.denot
if (select(d)) syms = syms union d
syms = syms union e.sym.denot
e = lookupNextEntry(e)
}
syms
Expand Down Expand Up @@ -458,10 +457,6 @@ object Scopes {
*/
def scopeTransform(owner: Symbol)(op: => MutableScope): MutableScope = op

val selectAll: SymDenotation => Boolean = alwaysTrue
val selectPrivate: SymDenotation => Boolean = d => (d.flagsUNSAFE is Flags.Private)
val selectNonPrivate: SymDenotation => Boolean = d => !(d.flagsUNSAFE is Flags.Private)

/** The empty scope (immutable).
*/
object EmptyScope extends Scope {
Expand Down
125 changes: 64 additions & 61 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1564,14 +1564,14 @@ object SymDenotations {
initPrivateWithin: Symbol)
extends SymDenotation(symbol, maybeOwner, name, initFlags, initInfo, initPrivateWithin) {

import util.LRUCache
import util.HashTable

// ----- caches -------------------------------------------------------

private var myTypeParams: List[TypeSymbol] = null
private var fullNameCache: SimpleIdentityMap[QualifiedNameKind, Name] = SimpleIdentityMap.Empty

private var myMemberCache: LRUCache[Name, PreDenotation] = null
private var myMemberCache: HashTable[Name, PreDenotation] = null
private var myMemberCachePeriod: Period = Nowhere

/** A cache from types T to baseType(T, C) */
Expand All @@ -1582,9 +1582,9 @@ object SymDenotations {
private var baseDataCache: BaseData = BaseData.None
private var memberNamesCache: MemberNames = MemberNames.None

private def memberCache(using Context): LRUCache[Name, PreDenotation] = {
private def memberCache(using Context): HashTable[Name, PreDenotation] = {
if (myMemberCachePeriod != ctx.period) {
myMemberCache = new LRUCache
myMemberCache = HashTable()
myMemberCachePeriod = ctx.period
}
myMemberCache
Expand Down Expand Up @@ -1855,61 +1855,53 @@ object SymDenotations {
* The elements of the returned pre-denotation all
* have existing symbols.
*/
final def membersNamed(name: Name)(using Context): PreDenotation = {
val privates = info.decls.denotsNamed(name, selectPrivate)
privates union nonPrivateMembersNamed(name).filterDisjoint(privates)
}

/** All non-private members of this class that have the given name.
* The elements of the returned pre-denotation all
* have existing symbols.
* @param inherited The method is called on a parent class from computeNPMembersNamed
*/
final def nonPrivateMembersNamed(name: Name)(using Context): PreDenotation = {
Stats.record("nonPrivateMembersNamed")
if (Config.cacheMembersNamed) {
var denots: PreDenotation = memberCache lookup name
if (denots == null) {
denots = computeNPMembersNamed(name)
final def membersNamed(name: Name)(using Context): PreDenotation =
Stats.record("membersNamed")
if Config.cacheMembersNamed then
var denots: PreDenotation = memberCache.lookup(name)
if denots == null then
denots = computeMembersNamed(name)
memberCache.enter(name, denots)
}
else if (Config.checkCacheMembersNamed) {
val denots1 = computeNPMembersNamed(name)
else if Config.checkCacheMembersNamed then
val denots1 = computeMembersNamed(name)
assert(denots.exists == denots1.exists, s"cache inconsistency: cached: $denots, computed $denots1, name = $name, owner = $this")
}
denots
}
else computeNPMembersNamed(name)
}
else computeMembersNamed(name)


private[core] def computeNPMembersNamed(name: Name)(using Context): PreDenotation = {
Stats.record("computeNPMembersNamed after fingerprint")
ensureCompleted()
val ownDenots = info.decls.denotsNamed(name, selectNonPrivate)
if (debugTrace) // DEBUG
/** All non-private members of this class that have the given name.
* The elements of the returned pre-denotation all have existing symbols.
*/
final def nonPrivateMembersNamed(name: Name)(using Context): PreDenotation =
val mbr = membersNamed(name)
val nonPrivate = mbr.filterWithFlags(EmptyFlags, Private)
if nonPrivate eq mbr then mbr
else addInherited(name, nonPrivate)

private[core] def computeMembersNamed(name: Name)(using Context): PreDenotation =
Stats.record("computeMembersNamed")
val ownDenots = info.decls.denotsNamed(name)
if debugTrace then
println(s"$this.member($name), ownDenots = $ownDenots")
def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match {
addInherited(name, ownDenots)

private def addInherited(name: Name, ownDenots: PreDenotation)(using Context): PreDenotation =
def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match
case p :: ps =>
val denots1 = collect(denots, ps)
p.classSymbol.denot match {
p.classSymbol.denot match
case parentd: ClassDenotation =>
denots1.union(
parentd.nonPrivateMembersNamed(name)
.mapInherited(ownDenots, denots1, thisType))
val inherited = parentd.nonPrivateMembersNamed(name)
denots1.union(inherited.mapInherited(ownDenots, denots1, thisType))
case _ =>
denots1
}
case nil =>
denots
}
if (name.isConstructorName) ownDenots
case nil => denots
if name.isConstructorName then ownDenots
else collect(ownDenots, classParents)
}

override final def findMember(name: Name, pre: Type, required: FlagSet, excluded: FlagSet)(using Context): Denotation = {
val raw = if (excluded.is(Private)) nonPrivateMembersNamed(name) else membersNamed(name)
override final def findMember(name: Name, pre: Type, required: FlagSet, excluded: FlagSet)(using Context): Denotation =
val raw = if excluded.is(Private) then nonPrivateMembersNamed(name) else membersNamed(name)
raw.filterWithFlags(required, excluded).asSeenFrom(pre).toDenot(pre)
}

/** Compute tp.baseType(this) */
final def baseTypeOf(tp: Type)(using Context): Type = {
Expand Down Expand Up @@ -2213,25 +2205,24 @@ object SymDenotations {
* object that hides a class or object in the scala package of the same name, because
* the behavior would then be unintuitive for such members.
*/
override def computeNPMembersNamed(name: Name)(using Context): PreDenotation = {
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
override def computeMembersNamed(name: Name)(using Context): PreDenotation =

def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match
case pcls :: pobjs1 =>
if (pcls.isCompleting) recur(pobjs1, acc)
else {
val pmembers = pcls.computeNPMembersNamed(name).filterWithPredicate { d =>
else
val pobjMembers = pcls.nonPrivateMembersNamed(name).filterWithPredicate { d =>
// Drop members of `Any` and `Object`
val owner = d.symbol.maybeOwner
(owner ne defn.AnyClass) && (owner ne defn.ObjectClass)
}
recur(pobjs1, acc.union(pmembers))
}
recur(pobjs1, acc.union(pobjMembers))
case nil =>
val directMembers = super.computeNPMembersNamed(name)
val directMembers = super.computeMembersNamed(name)
if !acc.exists then directMembers
else acc.union(directMembers.filterWithPredicate(!_.symbol.isAbsent())) match
case d: DenotUnion => dropStale(d)
case d => d
}

def dropStale(multi: DenotUnion): PreDenotation =
val compiledNow = multi.filterWithPredicate(d =>
Expand Down Expand Up @@ -2273,13 +2264,12 @@ object SymDenotations {
multi.filterWithPredicate(_.symbol.associatedFile == chosen)
end dropStale

if (symbol `eq` defn.ScalaPackageClass) {
val denots = super.computeNPMembersNamed(name)
if (denots.exists || name == nme.CONSTRUCTOR) denots
if symbol eq defn.ScalaPackageClass then
val denots = super.computeMembersNamed(name)
if denots.exists || name == nme.CONSTRUCTOR then denots
else recur(packageObjs, NoDenotation)
}
else recur(packageObjs, NoDenotation)
}
end computeMembersNamed

/** The union of the member names of the package and the package object */
override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
Expand Down Expand Up @@ -2325,6 +2315,13 @@ object SymDenotations {
override def owner: Symbol = throw new AssertionError("NoDenotation.owner")
override def computeAsSeenFrom(pre: Type)(using Context): SingleDenotation = this
override def mapInfo(f: Type => Type)(using Context): SingleDenotation = this

override def matches(other: SingleDenotation)(using Context): Boolean = false
override def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(using Context): SingleDenotation = this
override def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation = this
override def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation = this
override def filterWithFlags(required: FlagSet, excluded: FlagSet)(using Context): SingleDenotation = this

NoSymbol.denot = this
validFor = Period.allInRun(NoRunId)
}
Expand Down Expand Up @@ -2448,6 +2445,8 @@ object SymDenotations {
def apply(module: TermSymbol, modcls: ClassSymbol): LazyType = this

private var myDecls: Scope = EmptyScope
private var mySourceModule: Symbol = null
private var myModuleClass: Symbol = null
private var mySourceModuleFn: Context ?=> Symbol = LazyType.NoSymbolFn
private var myModuleClassFn: Context ?=> Symbol = LazyType.NoSymbolFn

Expand All @@ -2457,8 +2456,12 @@ object SymDenotations {
else sym.info.typeParams

def decls: Scope = myDecls
def sourceModule(using Context): Symbol = mySourceModuleFn
def moduleClass(using Context): Symbol = myModuleClassFn
def sourceModule(using Context): Symbol =
if mySourceModule == null then mySourceModule = mySourceModuleFn
mySourceModule
def moduleClass(using Context): Symbol =
if myModuleClass == null then myModuleClass = myModuleClassFn
myModuleClass

def withDecls(decls: Scope): this.type = { myDecls = decls; this }
def withSourceModule(sourceModuleFn: Context ?=> Symbol): this.type = { mySourceModuleFn = sourceModuleFn; this }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class Instrumentation extends MiniPhase { thisPhase =>
private val namesOfInterest = List(
"::", "+=", "toString", "newArray", "box", "toCharArray",
"map", "flatMap", "filter", "withFilter", "collect", "foldLeft", "foldRight", "take",
"reverse", "mapConserve", "mapconserve", "filterConserve", "zip")
"reverse", "mapConserve", "mapconserve", "filterConserve", "zip",
"denotsNamed", "lookup", "lookupEntry", "lookupAll", "toList")
private var namesToRecord: Set[Name] = _

private var consName: TermName = _
Expand Down
Loading