diff options
author | Martin Odersky <odersky@gmail.com> | 2017-04-02 19:11:34 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2017-04-11 09:33:12 +0200 |
commit | d0efabf2817468c248db8a2a6d5a6c0b58747867 (patch) | |
tree | 7f9c04d5631f1a204a8b343de1623cd2c57848e7 /compiler/src/dotty/tools/dotc/core | |
parent | 264211f6b7129923c01c2e3c402b157685d64b1f (diff) | |
download | dotty-d0efabf2817468c248db8a2a6d5a6c0b58747867.tar.gz dotty-d0efabf2817468c248db8a2a6d5a6c0b58747867.tar.bz2 dotty-d0efabf2817468c248db8a2a6d5a6c0b58747867.zip |
Lazy entering of names with internal $'s in package scopes
Names with internal $'s are entered in package scopes only if
- we look for a name with internal $'s.
- we want to know all the members of a package scope
This optimization seems to be fairly effective. The typical range
of package scopes that need $-names is between 0 and 20%. The optimization
seems to improve execution time of all unit tests by about 3%.
Also. drop the inheritance from Iterable to Scope. The reason
is that we now need a context parameter for toList and
other Iterable operations which makes them impossible to
fit into the Iterable framework.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core')
5 files changed, 101 insertions, 51 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index c80ad876a..b299de434 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -294,7 +294,7 @@ object Contexts { /** Is this a context that introduces a non-empty scope? */ def isNonEmptyScopeContext: Boolean = - (this.scope ne outer.scope) && this.scope.nonEmpty + (this.scope ne outer.scope) && !this.scope.isEmpty /** Leave message in diagnostics buffer if it exists */ def diagnose(str: => String) = diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index c256e7071..205798474 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -60,7 +60,7 @@ object Scopes { * or to delete them. These methods are provided by subclass * MutableScope. */ - abstract class Scope extends DotClass with printing.Showable with Iterable[Symbol] { + abstract class Scope extends DotClass with printing.Showable { /** The last scope-entry from which all others are reachable via `prev` */ private[dotc] def lastEntry: ScopeEntry @@ -76,18 +76,37 @@ object Scopes { /** The symbols in this scope in the order they were entered; * inherited from outer ones first. */ - def toList: List[Symbol] + def toList(implicit ctx: Context): List[Symbol] /** Return all symbols as an iterator in the order they were entered in this scope. */ - def iterator: Iterator[Symbol] = toList.iterator + def iterator(implicit ctx: Context): Iterator[Symbol] = toList.iterator + + /** Is the scope empty? */ + def isEmpty: Boolean = lastEntry eq null + + def foreach[U](p: Symbol => U)(implicit ctx: Context): Unit = toList foreach p + + def filter(p: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = { + ensureComplete() + var syms: List[Symbol] = Nil + var e = lastEntry + while ((e ne null) && e.owner == this) { + val sym = e.sym + if (p(sym)) syms = sym :: syms + e = e.prev + } + syms + } + + def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match { + case sym :: _ => sym + case _ => NoSymbol + } /** Returns a new mutable scope with the same content as this one. */ def cloneScope(implicit ctx: Context): MutableScope - /** Is the scope empty? */ - override def isEmpty: Boolean = lastEntry eq null - /** Lookup a symbol entry matching given name. */ def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry @@ -144,6 +163,12 @@ object Scopes { final def toText(printer: Printer): Text = printer.toText(this) def checkConsistent()(implicit ctx: Context) = () + + /** Ensure that all elements of this scope have been entered. + * Overridden by SymbolLoaders.PackageLoader#PackageScope, where it + * makes sure that all names with `$`'s have been added. + */ + protected def ensureComplete()(implicit ctx: Context): Unit = () } /** A subclass of Scope that defines methods for entering and @@ -341,8 +366,9 @@ object Scopes { /** Returns all symbols as a list in the order they were entered in this scope. * Does _not_ include the elements of inherited scopes. */ - override final def toList: List[Symbol] = { + override final def toList(implicit ctx: Context): List[Symbol] = { if (elemsCache eq null) { + ensureComplete() elemsCache = Nil var e = lastEntry while ((e ne null) && e.owner == this) { @@ -354,6 +380,7 @@ object Scopes { } override def implicitDecls(implicit ctx: Context): List[TermRef] = { + ensureComplete() var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { @@ -368,25 +395,13 @@ object Scopes { /** Vanilla scope - symbols are stored in declaration order. */ - final def sorted: List[Symbol] = toList - - override def foreach[U](p: Symbol => U): Unit = toList foreach p - - override def filter(p: Symbol => Boolean): List[Symbol] = { - var syms: List[Symbol] = Nil - var e = lastEntry - while ((e ne null) && e.owner == this) { - val sym = e.sym - if (p(sym)) syms = sym :: syms - e = e.prev - } - syms - } + final def sorted(implicit ctx: Context): List[Symbol] = toList override def openForMutations: MutableScope = this /** Check that all symbols in this scope are in their correct hashtable buckets. */ override def checkConsistent()(implicit ctx: Context) = { + ensureComplete() var e = lastEntry while (e != null) { var e1 = lookupEntry(e.name) @@ -425,7 +440,7 @@ object Scopes { override private[dotc] def lastEntry = null override def size = 0 override def nestingLevel = 0 - override def toList = Nil + override def toList(implicit ctx: Context) = Nil override def cloneScope(implicit ctx: Context): MutableScope = unsupported("cloneScope") override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = null override def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = null diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b8cd7bb18..5a277cacb 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -930,14 +930,15 @@ object SymDenotations { * and which is also defined in the same scope and compilation unit. * NoSymbol if this class does not exist. */ - final def companionClass(implicit ctx: Context): Symbol = { - val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first - - if (companionMethod.exists) - companionMethod.info.resultType.classSymbol - else - NoSymbol - } + final def companionClass(implicit ctx: Context): Symbol = + if (is(Package)) NoSymbol + else { + val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first + if (companionMethod.exists) + companionMethod.info.resultType.classSymbol + else + NoSymbol + } final def scalacLinkedClass(implicit ctx: Context): Symbol = if (this is ModuleClass) companionNamed(effectiveName.toTypeName) @@ -1777,8 +1778,8 @@ object SymDenotations { def constrNamed(cname: TermName) = info.decls.denotsNamed(cname).last.symbol // denotsNamed returns Symbols in reverse order of occurrence if (this.is(ImplClass)) constrNamed(nme.TRAIT_CONSTRUCTOR) // ignore normal constructor - else - constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) + else if (this.is(Package)) NoSymbol + else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) } /** The parameter accessors of this class. Term and type accessors, diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 168908ced..e4d2d446f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -14,6 +14,7 @@ import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util import StdNames._, NameOps._ import Decorators.{PreNamedString, StringInterpolators} import classfile.ClassfileParser +import util.Stats import scala.util.control.NonFatal object SymbolLoaders { @@ -148,46 +149,79 @@ class SymbolLoaders { override def sourceModule(implicit ctx: Context) = _sourceModule def description = "package loader " + classpath.name + private var enterFlatClasses: Option[Context => Unit] = None + + Stats.record("package scopes") + /** The scope of a package. This is different from a normal scope - * in three aspects: - * - * 1. Names of scope entries are kept in mangled form. - * 2. Some function types in the `scala` package are synthesized. + * in three aspects: + * + * 1. Names of scope entries are kept in mangled form. + * 2. Some function types in the `scala` package are synthesized. */ final class PackageScope extends MutableScope { override def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = super.newScopeEntry(name.mangled, sym) override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { - val e = super.lookupEntry(name.mangled) - if (e == null && - _sourceModule.name == nme.scala_ && _sourceModule == defn.ScalaPackageVal && - name.isTypeName && name.isSyntheticFunction) + val mangled = name.mangled + val e = super.lookupEntry(mangled) + if (e != null) e + else if (_sourceModule.initialDenot.name == nme.scala_ && _sourceModule == defn.ScalaPackageVal && + name.isTypeName && name.isSyntheticFunction) newScopeEntry(defn.newFunctionNTrait(name.asTypeName)) + else if (isFlatName(mangled.toSimpleName) && enterFlatClasses.isDefined) { + Stats.record("package scopes with flatnames entered") + enterFlatClasses.get(ctx) + lookupEntry(name) + } else e } + override def ensureComplete()(implicit ctx: Context) = + for (enter <- enterFlatClasses) enter(ctx) + override def newScopeLikeThis() = new PackageScope } private[core] val currentDecls: MutableScope = new PackageScope() + def isFlatName(name: SimpleTermName) = name.lastIndexOf('$', name.length - 2) >= 0 + + def isFlatName(classRep: ClassPath#ClassRep) = { + val idx = classRep.name.indexOf('$') + idx >= 0 && idx < classRep.name.length - 1 + } + + def maybeModuleClass(classRep: ClassPath#ClassRep) = classRep.name.last == '$' + + private def enterClasses(root: SymDenotation, flat: Boolean)(implicit ctx: Context) = { + def isAbsent(classRep: ClassPath#ClassRep) = + !root.unforcedDecls.lookup(classRep.name.toTypeName).exists + + if (!root.isRoot) { + for (classRep <- classpath.classes) + if (!maybeModuleClass(classRep) && isFlatName(classRep) == flat && + (!flat || isAbsent(classRep))) // on 2nd enter of flat names, check that the name has not been entered before + initializeFromClassPath(root.symbol, classRep) + for (classRep <- classpath.classes) + if (maybeModuleClass(classRep) && isFlatName(classRep) == flat && + isAbsent(classRep)) + initializeFromClassPath(root.symbol, classRep) + } + } + def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = { assert(root is PackageClass, root) - def maybeModuleClass(classRep: ClassPath#ClassRep) = classRep.name.last == '$' val pre = root.owner.thisType root.info = ClassInfo(pre, root.symbol.asClass, Nil, currentDecls, pre select sourceModule) if (!sourceModule.isCompleted) sourceModule.completer.complete(sourceModule) - if (!root.isRoot) { - for (classRep <- classpath.classes) - if (!maybeModuleClass(classRep)) - initializeFromClassPath(root.symbol, classRep) - for (classRep <- classpath.classes) - if (maybeModuleClass(classRep) && - !root.unforcedDecls.lookup(classRep.name.toTypeName).exists) - initializeFromClassPath(root.symbol, classRep) + enterFlatClasses = Some { ctx => + enterFlatClasses = None + enterClasses(root, flat = true)(ctx) } + enterClasses(root, flat = false) if (!root.isEmptyPackage) for (pkg <- classpath.packages) enterPackage(root.symbol, pkg) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f3bb99b27..1db3ebcb0 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -363,7 +363,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } def slowSearch(name: Name): Symbol = - owner.info.decls.find(_.name == name).getOrElse(NoSymbol) + owner.info.decls.find(_.name == name) def nestedObjectSymbol: Symbol = { // If the owner is overloaded (i.e. a method), it's not possible to select the |