aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-04-02 19:11:34 +0200
committerMartin Odersky <odersky@gmail.com>2017-04-11 09:33:12 +0200
commitd0efabf2817468c248db8a2a6d5a6c0b58747867 (patch)
tree7f9c04d5631f1a204a8b343de1623cd2c57848e7 /compiler/src/dotty/tools/dotc/core
parent264211f6b7129923c01c2e3c402b157685d64b1f (diff)
downloaddotty-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')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Contexts.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/Scopes.scala59
-rw-r--r--compiler/src/dotty/tools/dotc/core/SymDenotations.scala21
-rw-r--r--compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala68
-rw-r--r--compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala2
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